Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When Can I First Measure a View?

So I have a bit of confusion with trying to set the background drawable of a view as it is displayed. The code relies upon knowing the height of the view, so I can't call it from onCreate() or onResume(), because getHeight() returns 0. onResume() seems to be the closest I can get though. Where should I put code such as the below so that the background changes upon display to the user?

    TextView tv = (TextView)findViewById(R.id.image_test);     LayerDrawable ld = (LayerDrawable)tv.getBackground();     int height = tv.getHeight(); //when to call this so as not to get 0?     int topInset = height / 2;     ld.setLayerInset(1, 0, topInset, 0, 0);     tv.setBackgroundDrawable(ld); 
like image 534
Kevin Coppock Avatar asked Dec 09 '10 00:12

Kevin Coppock


People also ask

How do you measure a view?

The measured dimensions can be obtained by calling getMeasuredWidth() and getMeasuredHeight(). The second pair is simply known as width and height, or sometimes drawing width and drawing height. These dimensions define the actual size of the view on screen, at drawing time and after layout.

What does view Measure () do?

UNSPECIFIED : This is used by a parent to determine the desired dimension of a child View . For example, a LinearLayout may call measure() on its child with the height set to UNSPECIFIED and a width of EXACTLY 240 to find out how tall the child View wants to be given a width of 240 pixels.

What's the measurement in Android for views?

Each View has two different types of sizes: the measured width and height, which is basically how big it wants to be, and the width and height that are the actual dimensions they have after being laid out. The desired size cannot always be provided, e.g. a view could request a width larger than its parent's width.


1 Answers

I didn't know about ViewTreeObserver.addOnPreDrawListener(), and I tried it in a test project.

With your code it would look like this:

public void onCreate() { setContentView(R.layout.main);  final TextView tv = (TextView)findViewById(R.id.image_test); final LayerDrawable ld = (LayerDrawable)tv.getBackground(); final ViewTreeObserver obs = tv.getViewTreeObserver(); obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {     @Override     public boolean onPreDraw () {         Log.d(TAG, "onPreDraw tv height is " + tv.getHeight()); // bad for performance, remove on production         int height = tv.getHeight();         int topInset = height / 2;         ld.setLayerInset(1, 0, topInset, 0, 0);         tv.setBackgroundDrawable(ld);          return true;    } }); } 

In my test project onPreDraw() has been called twice, and I think in your case it may cause an infinite loop.

You could try to call the setBackgroundDrawable() only when the height of the TextView changes :

private int mLastTvHeight = 0;  public void onCreate() { setContentView(R.layout.main);  final TextView tv = (TextView)findViewById(R.id.image_test); final LayerDrawable ld = (LayerDrawable)tv.getBackground(); final ViewTreeObserver obs = mTv.getViewTreeObserver(); obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {     @Override     public boolean onPreDraw () {         Log.d(TAG, "onPreDraw tv height is " + tv.getHeight()); // bad for performance, remove on production         int height = tv.getHeight();         if (height != mLastTvHeight) {             mLastTvHeight = height;             int topInset = height / 2;             ld.setLayerInset(1, 0, topInset, 0, 0);             tv.setBackgroundDrawable(ld);         }          return true;    } }); } 

But that sounds a bit complicated for what you are trying to achieve and not really good for performance.

EDIT by kcoppock

Here's what I ended up doing from this code. Gautier's answer got me to this point, so I'd rather accept this answer with modification than answer it myself. I ended up using the ViewTreeObserver's addOnGlobalLayoutListener() method instead, like so (this is in onCreate()):

final TextView tv = (TextView)findViewById(R.id.image_test); ViewTreeObserver vto = tv.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {     @Override     public void onGlobalLayout() {         LayerDrawable ld = (LayerDrawable)tv.getBackground();         ld.setLayerInset(1, 0, tv.getHeight() / 2, 0, 0);     } }); 

Seems to work perfectly; I checked LogCat and didn't see any unusual activity. Hopefully this is it! Thanks!

like image 179
Gautier Hayoun Avatar answered Oct 14 '22 19:10

Gautier Hayoun