Android - Is there a callback function when a View is all set? - android

I have a custom View, in which I need to perform treatment that require that the size of the view is set (so onCreate doesn't works), but before displaying anything. So I wondered if there is a callback function tat I could override for that ?
Right now I'm using onLayout, but it seems to be called twice, so I'm assuming it's probably not the right use for this. I also tried doing it in onMeasure but it seems that getWidth() and getHeight still returns 0 in it.
Thkanks,
Robin.

Try to use OnGlobalLayoutListener inside onResume()
#Override
public void onResume(){
super.onResume();
ViewTreeObserver viewTreeObserver = yourView.getViewTreeObserver();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
viewWidth = yourView.getWidth();
viewHeight = yourView.getHeight();
yourView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
System.out.println(viewHeight + " on resume x" + yourView.getX());
}
});
}
}

You can use the callback OnSizeChanged.

use onSizeChanged,
#Override
protected void onSizeChanged(int width, int height, int oldwidth, int oldheight) {
super.onSizeChanged(width, height, oldwidth, oldheight);
// your code here.
}

Try those methods
1.
ViewTreeObserver vto2 = imageView.getViewTreeObserver();
vto2.addOnGlobalLayoutListener(new
OnGlobalLayoutListener() {
#Override
public void onGlobalLayout () {
imageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height = imageView.getHeight();
int width = imageView.getWidth();
textView.append("\n\n" + imageView.getHeight() + "," + imageView.getWidth());
}
}
);
2.
imageView.post(new Runnable() {
#Override
public void run () {
int h = imageView.getHeight();
int w = imageView.getWidth();
Log.i(TAG, "Height=" + h);
}
};

Related

Obtain width and height of a Layout

I have a FrameLayout, when I try to get its params like this:
ViewGroup.LayoutParams params = cameraPreviewFrameLayout.getLayoutParams();
int layoutHeight = params.height;
int layoutWidth = params.width;
Here, layoutHeight is -2 and layoutWidth is -1. This is my XML:
<FrameLayout
android:id="#+id/liveActivity_cameraPreviewFrameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>
I am performing this action from onCreate, but also I tried to to it through onStart(), with the same result. Obviously the height and width of this layout is not these values.
How can I retrieve the size of the layout effectively?
The values returned are correct, because:
-2 stands for LayoutParams.WRAP_CONENT
-1 stands for LayoutParams.MATCH_PARENT
If you want to get exact values, you'll need to use the methods getHeight and getWidth. However those methods can only be used once the layout has been measured, so delay your call like this:
cameraPreviewFrameLayout.post(new Runnable() {
#Override
public void run() {
View v = cameraPreviewFrameLayout;
Log.e("TAG", v.getWidth() + ":" + v.getHeight());
}
});
Proper way to getting size of layout after or inside onLayoutChange method call
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final FrameLayout layout = (FrameLayout) findViewById(R.id.liveActivity_cameraPreviewFrameLayout);
layout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
#Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
int width = right - left;
int height = bottom - top;
Log.v("TAG", String.format("%d - %d", width, height));
//Or
Log.v("TAG", String.format("%d - %d", layout.getWidth(), layout.getHeight()));
//And after this method call, calling layout.getWidth() and layout.getHeight() will give right values
}
});
}

How do i know, when view is drawn?

My Activity layout has View, i have get Y position this view at the beginning of the program, but if i shall insert in bottom method onCreate , i get position value = 0. How i can know, when view is drawn and get her Y position. I try get Y position when i click on button, position get good, but i have get position without using the button.
I get position:
private float cardsStartPosition = 0;
if (cardsStartPosition == 0) {
cardsStartPosition = commonCardContainer.getY();
}
Thanks for answer.
Try this method..It works fine for me.
ViewTreeObserver vto = YourView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//do your stuff here
}
});
Add a global layout listener to your view, you will get notified when layout is done and your view has dimensions ...
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//Here you can get your dimensions
int width = view.getWidth();
//View.removeOnGlobalLayoutListener(this);
}
});
You mention just View. So if that means it's your custom view, you can override the onLayout():
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
...;
}
This way you can observe any layout changes of your view. If you're also interested in size changes, there's also the onSizeChanged():
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
...;
}
If it's not your custom view, I'd go with the Ketan Ahir's way.

Get root dimension on profound child measure event

In the view hierarchy:
Window > ViewGroup (root) > ViewGroup[...] > View (child)
I need to know root dimension in a profound child onMeasure event.
Exemple:
#Override
protected void onMeasure(int wMS, int hMS) {
int desiredW = Math.round(rootW * factorW);
int desiredH = Math.round(rootH * factorH);
/* ... compute final dimensions ... */
setMeasuredDimension(finalW, finalH);
}
Note: At this moment, getRootView and getWindow dimentions equals to 0 because children have to setMeasuredDimention before their parents
Considering a lot of children needing this dimension, to do it:
I created an interface:
public interface OnRootSizeChanged {
public void onRootSizeChanged(int w, int h);
}
I implemented my child which now implements OnRootSizeChanged inteface:
private int rootW;
private int rootH;
#Override
public void onRootSizeChanged(int w, int h) {
rootW = w;
rootH = h;
}
I implemented root view:
#Override
protected void onMeasure(int wMS, int hMS) {
int w = MeasureSpec.getSize(wMS);
int h = MeasureSpec.getSize(hMS);
dispatchOnRootSizeChange(this, w, h);
super.onMeasure(wMS, hMS);
}
private void dispatchOnRootSizeChange(ViewGroup v, int w, int h) {
for (int i = 0, n = v.getChildCount(); i < n; i++) {
View child = v.getChildAt(i);
if (child instanceof OnRootSizeChanged)
((OnRootSizeChanged) child).onRootSizeChanged(w, h);
if (child instanceof ViewGroup)
dispatchOnRootSizeChange((ViewGroup) child, w, h);
}
}
My question is:
Have I simpler way to do this without recursivity or with better practice ?
Update: This method is invalid in case of ViewPager element in ViewGroup[...] breabcrumb. When ViewPager instantiate children pages, they have not yet received OnRootSizeChanged event so:
Children have to ask the root dimension, no the root to tell his dimension to their children
So I searched how to target root from a profound child to ask him:
getRootView() not seems targeting the view attached with setContentView()
getWindow().getDecorView() either
One possible way is:
On child:
#Override
protected void onMeasure(int wMS, int hMS) {
ViewParent parent = getParent();
while (parent instanceof RootViewClass == false)
parent = parent.getParent();
RootViewClass root = (RootViewClass) parent;
int desiredW = Math.round(root.w * factorW);
int desiredH = Math.round(root.h * factorH);
/* ... compute final dimensions ... */
setMeasuredDimension(finalW, finalH);
}
On root instance of RootViewClass:
public int w, h;
#Override
protected void onMeasure(int wMS, int hMS) {
w = MeasureSpec.getSize(wMS);
h = MeasureSpec.getSize(hMS);
super.onMeasure(wMS, hMS);
}
But with lot of children, I don't think this is a good practice. If I could find root view without use of loop.
You can forward the parent's size by storing those values in the onMeasure() method as you receive them and then letting the children access the values in their onMeasure() method through the Context reference:
// simple interface
public interface ParentRef {
void YourViewGroup getRoot();
}
// the Activity implements the interface above
public class YourActivity extends Activity implements ParentRef {
private YourViewGroup mRoot;
//in onCreate initialize the mRoot reference
#Override
public YourViewGroup getRoot() {
return mRoot;
}
//... rest of the Activity
}
// the custom ViewGroup will store the dimensions:
//fields in the root view
private int mCurWidth;
private int mCurHeight;
#Override
protected void onMeasure(int wMS, int hMS) {
int w = MeasureSpec.getSize(wMS);
int h = MeasureSpec.getSize(hMS);
mCurWidth = w;
mCurHeight = h;
// now as the children are measured they can see the values above
super.onMeasure(wMS, hMS);
}
public int getStoredWidth() {
return mCurWidth;
}
public int getStoredHeight() {
return mCurHeight;
}
// in the children's onMeasure simply do:
#Override
protected void onMeasure(int wMS, int hMS) {
final YourViewGroup root = ((ParentRef) getContext()).getRoot();
//width root.getStoredWidth()
// height root.getStoredHeight()
/* ... compute final dimensions ... */
setMeasuredDimension(finalW, finalH);
}
You can use a ViewTreeObserver (http://developer.android.com/reference/android/view/ViewTreeObserver.html)
//...get your view, than attach a viewTreeObserver on it!
ViewTreeObserver vto = view.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//misure the view here, like view.getHeight()
}
});

View's getWidth() and getHeight() returning 0

I have looked at the similar questions and tried their solutions, but it didn't work for me. I'm trying to read width of an imageView, but it is returning 0. Here is the code:
public class MainActivity extends Activity {
private ImageView image1;
float centerX,centerY;
private ImageView image2;
private boolean isFirstImage = true;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.rotate);
image1 = (ImageView) findViewById(R.id.ImageView01);
image2 = (ImageView) findViewById(R.id.ImageView02);
image2.setVisibility(View.GONE);
if (isFirstImage) {
applyRotation(0, 90);
isFirstImage = !isFirstImage;
}
}
private void applyRotation(float start, float end) {
centerX=image1.getWidth()/2.0f;
centerY=image1.getHeight()/2.0f;
final Flip3dAnimation rotation =
new Flip3dAnimation(start, end,centerX , centerY);
rotation.setDuration(500);
rotation.setFillAfter(true);
rotation.setInterpolator(new AccelerateInterpolator());
rotation.setAnimationListener(new DisplayNextView(isFirstImage, image1,
image2));
if (isFirstImage)
{
image1.startAnimation(rotation);
} else {
image2.startAnimation(rotation);
}
}
}
Can anyone help me with this? Thanks
You need to use the onSizechanged() method.
This is called during layout when the size of this view has changed
You should use: image1.getLayoutParams().width;
You are either accessing the ImageViews width and height before it has been measured, or after you set its visibility to GONE.
image2.setVisibility(View.GONE);
Then getWidth() or getHeight() will return 0.,
You can also have a look at onWindowFocusChanged(), onSizeChanged() and onMeasure():
#Override
public void onWindowFocusChanged(boolean focus) {
super.onWindowFocusChanged(focus);
// get the imageviews width and height here
}
public class GETImageView extends ImageView {
public GETImageView (Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Drawable d = getDrawable();
if (d != null) {
int width = MeasureSpec.getSize(widthMeasureSpec);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
The ImageView have not yet draw the image, so no width or height return.
You may use a subclass to get the imageview width.
Here is some link provide you another easy methods:
How to get the width and height of an android.widget.ImageView?
How to get the width and height of an Image View in android?
Android ImageView return getWidth, getHeight zero
Hope this help you.
you can use OnGlobalLayoutListener for the ImageView image1 so you can get exact width and height occupied by the view in the layout...as
image1 = (ImageView) findViewById(R.id.ImageView01);
ViewTreeObserver viewTreeObserver = image1.getViewTreeObserver();
viewTreeObserver
.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (isFirstImage) {
applyRotation(0, 90);
isFirstImage = !isFirstImage;
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN)
image1.getViewTreeObserver()
.removeOnGlobalLayoutListener(this);
else
image1.getViewTreeObserver()
.removeGlobalOnLayoutListener(this);
}
});
By the way, views don't get sized until the measurement steps have been taken. See this link:
getWidth() and getHeight() of View returns 0
Maybe you should call View.measure(int,int) first.
Since the width and height is not defined until this view is draw. You can call measure by hand to enforce this view to calculate it's size;
Please using a Handler and get height and width on this with delay time >= 10
This may be useful for use:
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
getHeight();
}
private void getHeight() {
// TODO Auto-generated method stub
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Height:" + mAnimationView.getMeasuredHeight(),
Toast.LENGTH_SHORT).show();
}
}, 10L);
}

Measure view in fragment

I need to know ImageView width and height. Is there a way to measure it in fragment ? In standard Activity I use this :
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
image.getWidth();
image.getHeight();
}
But where can I use image.getWidth(); and image.getHeight(); in fragment ?
Use the GlobalLayoutListener. You assign the listener to your ImageView in onCreateView() just like you do in that answer in the link. It also is more reliable than onWindowFocusChanged() for the main Activity so I recommend switching strategies.
EDIT
Example:
final View testView = findViewById(R.id.view_id);
ViewTreeObserver vto = testView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Log.d("TEST", "Height = " + testView.getHeight() + " Width = " + testView.getWidth());
ViewTreeObserver obs = testView.getViewTreeObserver();
obs.removeGlobalOnLayoutListener(this);
}
});

Categories

Resources