Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: get height of a view before it´s drawn

For an animation I need to know the height from a View. The Problem is, that the getHeight() method allways return 0 unless the View is drawn. So is there any way to get height without drawing it?

In this case the View is a LinearLayout.

EDIT: I try to adapt the expand Animation from https://github.com/Udinic/SmallExamples/blob/master/ExpandAnimationExample/src/com/udinic/expand_animation_example/ExpandAnimation.java

With it I want to expand some more informations for a list item. I wasn´t able to achieve the same effect via xml. At the moment the Animation only works when you know the layout size before drawing it.

like image 386
anonymous Avatar asked Mar 05 '12 23:03

anonymous


People also ask

How do you find the height and width of a view?

The width and height can be obtained by calling getWidth() and getHeight().

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'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.


4 Answers

Sounds like you want to get the height but hide the view before it is visible.

Have the visibility in the view set to visible or invisible to start with(just so that a height is created). Don't worry we will change it to invisible/gone in the code as shown below:

private int mHeight = 0; private View mView;  class...  // onCreate or onResume or onStart ... mView = findViewByID(R.id.someID); mView.getViewTreeObserver().addOnGlobalLayoutListener(      new OnGlobalLayoutListener(){          @Override         public void onGlobalLayout() {             // gets called after layout has been done but before display             // so we can get the height then hide the view               mHeight = mView.getHeight();  // Ahaha!  Gotcha              mView.getViewTreeObserver().removeGlobalOnLayoutListener( this );             mView.setVisibility( View.GONE );         }  }); 
like image 191
Jim Baca Avatar answered Sep 17 '22 21:09

Jim Baca


I had a similar issue with a similar situation.

I had to create an anmiation for which I required the height of the view that is subject to the animation. Within that view, which is a LinearLayout may be child view of type TextView. That TextView is multi line and the number of lines actually required varies.

Whatever I did I just got the measured height for the view as if the TextView has just one line to fill. No wonder this worked fine every time when the text happend to be short enough for a single line, but it failed when the text was longer than on line.

I considered this answer: https://stackoverflow.com/a/6157820/854396 But that one did not work too well for me because I am not able to access the embedded TextView directly from here. (I could have iterated though the tree of child views though.)

Have a look at my code as it works for now:

view is the view that is to be animated. It is a LinearLayout (The final solution will be a bit more type save here) this is a custom view that contains view and inherits from LinearLayout too.

   private void expandView( final View view ) {        view.setVisibility(View.VISIBLE);        LayoutParams parms = (LayoutParams) view.getLayoutParams();       final int width = this.getWidth() - parms.leftMargin - parms.rightMargin;        view.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),               MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));        final int targetHeight = view.getMeasuredHeight();        view.getLayoutParams().height = 0;        Animation anim = new Animation() {          @Override          protected void applyTransformation( float interpolatedTime, Transformation trans ) {             view.getLayoutParams().height =  (int) (targetHeight * interpolatedTime);             view.requestLayout();          }           @Override          public boolean willChangeBounds() {             return true;          }       };        anim.setDuration( ANIMATION_DURATION );       view.startAnimation( anim );    } 

This was nearly it but I made another mistake. Within the view hierarchy there are two custom views involved which are subclasses of LinearLayout and their xml is merged into with a merge tag as xml root tag. Within one of these merge tags I overlooked to set the android:orientation attribute to vertical. That did not bother too much for the layout itself, because within this merged ViewGroup was just one child ViewGroup and therfore I could not actually see the wrong orientation on screen. But it was there and sort of broke this layout measurement logic a bit.

In addition to the above it may be required to get rid of all paddings associated with LinearLayouts and subclasses within the view hierarchy. Replace them by margins, even if you have to wrap another layout around it to make it look the same.

like image 40
Hermann Klecker Avatar answered Sep 21 '22 21:09

Hermann Klecker


Technically, you can call measure method on the view and then get its height via getMeasureHeight. See this for more info: https://developer.android.com/guide/topics/ui/how-android-draws.html. You will need to give it a MeasureSpec though.

But in reality, the view's size is influenced by its parent layout, so you may get a different size than when it's actually drawn.

Also, you may try using RELATIVE_TO_SELF values in your animations.

like image 37
Fixpoint Avatar answered Sep 21 '22 21:09

Fixpoint


static void getMeasurments(View v) {

    v.measure(View.MeasureSpec.makeMeasureSpec(PARENT_VIEW.getWidth(), 
      View.MeasureSpec.EXACTLY),
         View.MeasureSpec.makeMeasureSpec(0, 
      View.MeasureSpec.UNSPECIFIED));

    final int targetHeight = v.getMeasuredHeight();
    final int targetWidth = v.getMesauredWidth();
}
like image 20
Anubhav Avatar answered Sep 21 '22 21:09

Anubhav