I have a custom FrameLayout class which does some custom drawing on the OnSizeChanged event. It's done there and not in the OnDraw event to avoid reentrancy and therefore performance issues. This works fine in a single activity application but fails in a TabActivity. The activity in the current tab renders fine but activities in non-active tabs are not being rendered. Here you can download an example project reproducing this. The project is HelloTabWidget but uses HelloAndroid project as well.
Found that onWindowVisibilityChanged event might help but need to find adequate size to plot my control. In the code snippet below I'd need a way to find container's size at onWindowVisibilityChanged.
#Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
int width = this.getWidth();
int height = this.getHeight();
drawContent(getCtxt(), width, height);
}
The solution was using OnDraw event doing some refactoring to the component and using a boolean flag to determine when the canvas is dirty to avoid unneeded reentrancy. SetWillNotDraw had to be set to false as well.
Related
Basically my goal is to set some custom View size exactly to 1/3 of the screen for portrait orientation and 1/6 for landscape
The first thought that came to my mind is to simply calculate some mSize variable and set it to the View in OnMeasure like this:
mSize = (AContext.getScreenSize().x / (orientation == Configuration.ORIENTATION_PORTRAIT ? 3 : 6)
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(mSize|MeasureSpec.EXACTLY, mSize|MeasureSpec.EXACTLY);
}
And it's working just fine, till i rotate the screen. The thing is that when screen is rotating View initialization and sizing are called before the orientation value will actually change, so the result is that it goes one third to landscape also, if it was opened with portrait orientation firstly, and other way around.
I'm aware that i can do something like :
android:configChanges="orientation|screenSize"
In AndroidManifest for my Activity and override onConfigurationChanged to handle the rotation and change mSize value there, but, it disables auto choosing between portrait and landscape layouts(from .xml files on inflating). So i'm ending up with the same xml file for both orientations.
Is there a way to keep orientation value from context.getResources().getConfiguration().orientation consistent?
Or i need to do some workaround with onConfigurationChange to enable layout choosing?(i don't want to do it maually in code thou)
Or is there a better way to achieve my goal, and i'm just doing it wrong from the start?
Please help me.
first I would say to not use android:configChanges="orientation|screenSize" on this situation. It makes no sense such a thing on a custom view.
Secondly I believe the main mistake is the way you're capturing the 3, or 6 values. As you can achieve it much simpler using XML.
res/values/integers.xml
<integer name="view_fraction">3</integer>
res/values-land/integers.xml
<integer name="view_fraction">6</integer>
Then during your view constructor you simply call:
int val = context.getResources().getInteger(R.integer.view_fraction)
Furthermore, I will offer some other suggested improve on your view size calculation by suggesting you to simply use code that Google provides us, instead of trying to re-code it. Using the classes from android.support.percent
If your custom view extends from FrameLayout or RelativeLayout I would suggest you to instead extend from their percent counterpart PercentFrameLayout and PercentRelativeLayout.
If the custom view does not extends from one of those, you can use their helper PercentLayoutHelper following the guide on their page.
That way you can easily dinamically assing percentage of view size on your XML layout
I have two views which overlap each other and one of them acts as a master to other. I am translating all the pinching and panning to scale and adjust the master view.
Now I wanna send this info over to the slave(Subclass of android.view.View). Being said that the other view doesn't respond to this dimensions. I have tried following methods
ViewGroup.LayoutParams params = this.getLayoutParams();
params.height = height;
params.width = width;
this.setLayoutParams(params);this.requestLayout();
setMeasuredDimension(width, height);
this.setMinimumHeight(height);
this.setMinimumWidth(width)
None of these seem to work.
Using the onTouchEvent I resize my TextureView and send the same to the other View object. On getting this Info about change in size which is in most cases greater than the parent dimensions(and screen size) the other view should also increase/change its size.
The subclassed view doesn't grow to the dimensions supplied to it. Moreover the increase is limited to the parent size(in my case its width: 1080 height: 1776) while it can go above that in the textureView which uses
textureView.setMinimumWidth(newWidth);
textureView.setMinimumHeight(newHeight);
Any ideas as to what maybe going wrong or a trusted way to change layout/dimensions across two views?
setLayoutParams => that would be the canonical way to position a View inside its parent.
setMeasuredDimension => this is used from View.onMeasure to tell the layout system what size your particular View wants. Use only if you're implementing your own View subclass.
setMinimumWidth/setMinimumHeight => tells the default View implementation what minimum size the View should have. If you implement View.onMeasure yourself, that shouldn't be needed.
All in all, if you stick with the standard Android framework components, setLayoutParams should be the place to look into.
The methods you've listed are all valid ways of setting sizes if used correctly, and in the right situations.
This is difficult to solve without further information.
In what way isn't it working? Is it causing a crash, or are the view sizes just not what you expect?
Where does that snippet sit in your code? I would guess its part of a custom class of your own which extends ViewGroup, but as you're new to stackoverflow we have no idea of your level of programming experience. It might be in completely the wrong place, and this refers to some other object entirely.
One thing I can suggest you check :
You mention your slave is a subclass of android.view.View, but this on its own is not enough to be able to use ViewGroup.LayoutParams. You need to ensure your slave is a direct or indirect subclass of android.view.ViewGroup.
Provide more of your code and info on what you are observing, and we will try and help you further.
Update:
Ok, so it's a TextureView that you are trying to resize.
I'd recommend you check the reported size of your TextureView after you set it's height and width. You could do this with break-points and run a debug, or you could use myTextureView.getWidth() and .getHeight(), and use Log statments to output them to the LogCat.
You can also check the size of the SurfaceTexture associated with your TextureView. Set a SurfaceTextureListener on your TextureView. When this listener is triggered, onSurfaceTextureAvailable() will provide the width and height of the SurfaceTexture.
I suggest this because you may find that you are able to change the size of the TextureView, but it is the content of the the TextureView that is not changing size. In this case you can override TextureView.onSizeChanged(int w, int h, int oldw, int oldh) to define the necessary transform to scale your content between the old and new sizes.
I am writing a game in which i need to check collisions between two views. when i use the view.getHitRect(outputRect) method from some reason the bottom padding of the view (all the other paddings are set to 0) is added to the top property of the view's hit rectangle. I've tried to fix this by overriding the getHitRect method:
public abstract class GameView extends ImageView {
#Override
public void getHitRect(Rect outRect) {
super.getHitRect(outRect);
outRect.top+=getPaddingBottom();
}
This got me a better result, but its still inaccurate, and the bigger the padding gets the less accurate the rectangle becomes, and the collision always occur too low. Before using paddings I was trying to do it without it, which gave me accurate hitRect but i got a different bug. Thanks in advance
Solved it after some trial and error. Apparently the padding was added to the rectangle, but it was divided equally between the rect.top and rect.bottom, so the solution was:
#Override
public void getHitRect(Rect outRect) {
super.getHitRect(outRect);
outRect.top+=getPaddingBottom()/2;
outRect.bottom-=getPaddingBottom()/2;
}
If one of you Android experts care to explain why I would be grateful, since I don't understand how it works and I just can't see the logic in it.
Is there a way the this problem can be fixed? I tried invalidate() but, it still displays the same problem. What happens is that, after opening the page/ the Activity, the images behaves like the one in Figure A. It only renders to my desired layout (Figure B) after scrolling it back and forth.
What I'm trying to do is set the width and heigth of the image during runtime. So this is also in relation to my previous questions : Images in my HorizontalListView changes it size randomly and ImageView dynamic width and height which received a very little help.
Any tips regarding this matter, please?
EDIT: btw, my classes are:
MyCustomAdapter (extends baseadapter, this calls the displayimage() from ImageLoader ),
MyActivity and
ImageLoader (this is where my image url are loaded, decoded, displayed asynchronously)
Im also confused as to where i will set the height and width of the imageView. For now, i set it at ImageLoader. It was okay. but i dont know if i did the right thing.
If you want to set the width and height manually at runtime, grab a reference to the ImageView's LayoutParams AFTER the View has been measured by the layout system. If you do this too early in the rendering phase, your view's width and height as well as its parent view and so on will be 0.
I have some code in an open source library that might help you. The process is two parts:
Set up an OnPreDrawListener attached to the ViewTreeObserver for your control. My example does this inside of a custom control, but you can do this in your activity as well.
Inside the onPreDraw method, your image and it's parent will now have their width and height values assigned to them. You can make your calculations and then set your width and/or height manually to the LayoutParams object of your view (don't forget to set it back).
Check out this example where I'm applying an aspect ratio to a custom ImageView just before it's rendered to the screen. I don't know if this exactly fits your use case, but this will demonstrate how to add an OnPreDrawListener to a ViewTreeObserver, removing it when you're done, and applying dynamic sizing to a View at runtime
https://github.com/aguynamedrich/beacon-utils/blob/master/Library/src/us/beacondigital/utils/RemoteImageView.java#L78
Here's a modified version that removes my particular resizing logic. It also grabs the ViewTreeObserver from the imageView, which is a more likely scenario if you're not implementing a custom control and you only want to do this in the Activity
private void initResizeLogic() {
final ViewTreeObserver obs = imageView.getViewTreeObserver();
obs.addOnPreDrawListener(new OnPreDrawListener() {
public boolean onPreDraw() {
dynamicResize();
obs.removeOnPreDrawListener(this);
return true;
}
});
}
protected void dynamicResize() {
ViewGroup.LayoutParams lp = imageView.getLayoutParams();
// resize logic goes here...
// imageView.getWidth() and imageView.getHeight() now return
// their initial layout values
lp.height = someCalculatedHeight;
lp.width = someCalculatedWidth;
imageView.setLayoutParams(lp);
}
}
I'm using OpenGL to dynamically render images on a GLSurfaceView within android.
The application runs perfectly fine for (static) XML-based layout with multiple instances of a custom GLSurfaceView. (top left- and top right on the image)
If these instances get dynamically interchanged by the visibility value, the last visible OpenGL-image is still on top of the new one. (bottom left- and bottom right on the image)
top left picture: 4 instances, normal size
top right picture: 4 instances, big size
bottom left picture: 1 instance, normal size (top left picture as unwanted overlay)
bottom right picture: 1 instance, big size (top left picture as unwanted overlay)
What I tried so far:
did not remove the unwanted instances:
hide unused images by android::visibility="gone" (does not work smooth)
move the views out of the visible area and resize them to 0 by 0
use plain colors instead of dynamic images to simplify the output
force a redraw of the view by invalidating it (I actually tried almost every function a view offers)
clear various buffers in the onDraw()-function (I actually tried almost every function the GLSurfaceView offers)
force a onPause()-event to stop the renderer
use the ViewPager to switch between the views
removed the unwanted instances successfully:
restart OpenGL by reentering the app (can't be used like this)
recursively hide all other GLSurfaceViews by android::visibility="gone" (bugged the engine, so it stopped working)
The unwanted images does not change with layout reflows like a visibility change of a view.
They are only visible if a GLSurfaceView is over another GLSurfaceView (hidden by android::visibility="gone").
There is no problem if a ImageView is used instead.
The first created instance do not have this problem, because it is on top (or bottom?) of the child-stack and is on top of its siblings.
I guess android supports only one OpenGL based view which is used by all instances of GLSurfaceView.
All instances seem to share some preferences (especially the visibility), so it can't just turned off or moved out.
GLSurfaceView-class:
public class Panel extends GLSurfaceView implements Renderer {
private static native void nativeRender();
public Panel(Context context) {
super(context);
this.setRenderer(this);
this.setRenderMode(RENDERMODE_WHEN_DIRTY);
}
public void onDrawFrame(GL10 gl) {
nativeRender();
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
gl.glViewport(0, 0, w, h);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
super.surfaceDestroyed(holder);
}
public void callback() {
this.requestRender();
}
}
So is it possible to use multiple OpenGL-views within (especially on top) of each other? is there a better way to interchange them without the use of the visibility value?
I was having same problem and couldn't solved the problem as i finally understand that one GLSurfaceView will be one top if we make another GLSurfaceView's visibility GONE & it won't be coming back on top of screen.
But i solved the problem with a trick. Instead of hiding the view, i changed it's width and height to 1 px, it's makes the view invisible to user. And when i need to show it , i changed it width & height via LayoutParams. And it partially solved my problem. I am showing you my code
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/smallVideoRenderLayoutIncoming"
android:layout_width="1dp"
android:layout_height="1dp"
android:background="#color/transparent"
android:orientation="vertical"/>
<LinearLayout
android:id="#+id/largeVideoRenderLayoutIncoming"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"/>
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ConstantFunctions.dpToPx(120),ConstantFunctions.dpToPx(170));
smallVideoRenderLayoutIncoming.setLayoutParams(layoutParams);
When i need to show it, i changed it's size as i needed.