24

After measuring a View with a constant dimensions with view.measure(), the getMeasuredHeight() and getMeasureWidth() is returning 0.

layout_view.xml, layout which is inflated to create the view

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="100dp"
    android:layout_height="100dp">
</FrameLayout>

function which measures the dimensions

public void measureView(Context context){
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    View view = inflater.inflate(R.layout.layout_view,null,false);

    view.measure( View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);

    Log.d(TAG,"Error width : " + view.getMeasuredWidth());
    Log.d(TAG,"Error height : " + view.getMeasuredHeight());

}
Boken
  • 4,825
  • 10
  • 32
  • 42
SathMK
  • 1,171
  • 1
  • 8
  • 18

7 Answers7

17

When you call view.getMeasuredWidth() in onCreate() or onCreateView(), the view has not been drawn yet. So you need to add a listener and will get a callback when the view is being drawn. Just like this in my code:

final ViewTreeObserver vto = view.getViewTreeObserver();
if (vto.isAlive()) {
    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            int viewWidth = view.getMeasuredWidth();
            // handle viewWidth here...

            if (Build.VERSION.SDK_INT < 16) {
                vto.removeGlobalOnLayoutListener(this);
            } else {
                vto.removeOnGlobalLayoutListener(this);
            }
        }
    });
}

NOTE: Remove the listener for better performance!

Don't call vto.removeOnGlobalLayoutListener(this) to remove the listener. Call it like this:

vto.getViewTreeObserver()
amnn
  • 3,657
  • 17
  • 23
Francis Shi
  • 395
  • 4
  • 8
  • I tried this and it seemed to work in all tested cases but now I see the getMeasuredWidth() returning 0 in production for some users :( – Sebas LG Sep 12 '16 at 09:33
  • Thank you! I was using ```OnPreDrawListener``` before (but seems it is not compatible with some devices 4.4.4 and 6.0.1). Using ```onGlobalLayout``` and removing the listener just when ```height > 0``` it works perfectly. – JCarlosR Nov 16 '16 at 22:18
  • 2
    What is `v`? It came out of nowhere. – Damn Vegetables Mar 06 '18 at 14:45
  • For some reason `getMeasuredWidth`only returns the "right" value after scrolling take place. Is there another way to trigger a correct measurement? – rraallvv Apr 04 '18 at 11:42
6

Are you measuring the view in onCreate(). The view isn't drawn yet. You have to wait until a time after the view is drawn before you can measure it.

Simple solution for this is to post a runnable to the layout. The runnable will be executed after the layout has happened.

For more info See this post

Edit try to change

view.measure( View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);

to

view.measure( View.MeasureSpec.EXACTLY, View.MeasureSpec.EXACTLY);
Community
  • 1
  • 1
Giru Bhai
  • 14,370
  • 5
  • 46
  • 74
  • i am measuring the the view in in onclick method. To avoid the issue you pointed out i am explicitly measuring the view with `view.measure()` – SathMK Jun 26 '14 at 12:39
  • i tried this may also, but still the result is zero – SathMK Jun 26 '14 at 13:16
4

Try this way,hope this will help you to solve your problem.

view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
     int width = view.getMeasuredWidth();
     int height = view.getMeasuredHeight();

   }
});
Haresh Chhelana
  • 24,720
  • 5
  • 57
  • 67
1

if your measured view has visibility set to gone, measuring will return 0. You can set it to visible, measure, and then set back to gone, all from code.

Radu Simionescu
  • 4,518
  • 1
  • 35
  • 34
1

You can not use View.MeasureSpec.* directly in measure call. Instead first makeMeasureSpec() and then use it to invoke measure():

final int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT/*can be match_parent or some exact value*/, View.MeasureSpec.UNSPECIFIED);
final int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT/*can be match_parent or some exact value*/, View.MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec, heightMeasureSpec);
M-Wajeeh
  • 17,204
  • 10
  • 66
  • 103
  • Thanks, but currently Kotling restricts passing this value (WRAP_CONTENT = -2 < 0). In my case https://stackoverflow.com/a/53739033/2914140 helped in `onCreateView()`, simply called view.measure(0, 0). In other events you should use `getViewTreeObserver()`, see https://stackoverflow.com/questions/14592930/getwidth-returns-0-if-set-by-androidlayout-width-match-parent. – CoolMind May 28 '19 at 11:50
  • ```View.MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED);```not work, it says ```Value must be ≥ 0 (was -2)``` – DawnYu May 25 '21 at 01:42
0

Just in case someone was experiencing this problem with ImageView:

I noticed that if your ImageView doesn't have "android:src" set (i.e. you change it dynamically) it will always return zero from getMeasuredWidth/Height.

A simple workaround would be to set a placeholder for view.

user3486184
  • 2,147
  • 3
  • 26
  • 28
0

You can use getViewTreeObserver in onCreate/onCreateView and other events.

fun setGlobalLayoutListener(view: View, method: () -> Unit) {
    val vto1 = view.viewTreeObserver
    if (vto1.isAlive) {
        vto1.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                val vto2 = view.viewTreeObserver
                if (vto2.isAlive) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                        vto2.removeOnGlobalLayoutListener(this)
                    } else {
                        @Suppress("DEPRECATION")
                        vto2.removeGlobalOnLayoutListener(this)
                    }
                    // Your actions are here:
                    method()
                }
            }
        })
    }
}

And call it: setGlobalLayoutListener(view) {updateUI(view)}.

I also tried

view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)

and then called view.control.measuredWidth in onCreateView()/onViewCreated(). But in some emulators (API 19) it returned wrong results.

CoolMind
  • 26,736
  • 15
  • 188
  • 224