View.measure() 后 getMeasuredHeight() 和 getMeasuredWidth() 返回 0

24

使用 view.measure() 测量一个具有固定尺寸View 后,getMeasuredHeight()getMeasuredWidth() 返回值为 0。

layout_view.xml 是用来填充创建该视图的布局文件。

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

测量尺寸的函数

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());

}

1
请查看此链接:https://dev59.com/HmUq5IYBdhLWcg3wT-6D - Giru Bhai
@GiruBhai,我尝试使用ViewTreeObserver,但它也不起作用。 - SathMK
View.MeasureSpec.* 不能直接使用,查看下面的答案。 - M-Wajeeh
7个回答

17

当你在onCreate()或者onCreateView()方法中调用view.getMeasuredWidth()时,view还没有被绘制出来。因此,你需要添加一个监听器并在view被绘制时得到回调。就像我的代码一样:

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);
            }
        }
    });
}

注意: 为了提高性能,请移除监听器!

不要调用vto.removeOnGlobalLayoutListener(this) 来移除监听器。请按照以下方式调用:

vto.getViewTreeObserver()

我尝试过这个方案,在所有测试用例中似乎都可以工作,但现在我发现在生产环境中,一些用户的 getMeasuredWidth() 返回值为 0 :( - Sebas LG
谢谢!我之前使用的是OnPreDrawListener(但似乎它与某些设备4.4.4和6.0.1不兼容)。现在使用onGlobalLayout,并在height > 0时移除监听器,它完美地工作了。 - JCarlosR
2
v是什么?它从哪里来的? - Damn Vegetables
由于某些原因,只有在滚动发生后,getMeasuredWidth才会返回“正确”的值。是否有其他方法可以触发正确的测量? - rraallvv

7
你是否在 onCreate() 中测量视图?视图尚未绘制。你必须等到视图绘制完成之后才能测量它。
简单的解决方案是将一个可运行对象发布到布局中。这个可运行对象将在布局发生后执行。
更多信息请参见此帖子 编辑 尝试更改
view.measure( View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);

to

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

我正在onclick方法中测量视图。为了避免你指出的问题,我明确地使用view.measure()来测量视图。 - SathMK
我也尝试了这个方法,但结果仍然是零。 - SathMK

4
尝试这种方法,希望能帮助您解决问题。
view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
   @Override
   public void onGlobalLayout() {
     int width = view.getMeasuredWidth();
     int height = view.getMeasuredHeight();

   }
});

1
如果您的视图设置为gone,测量将返回0。您可以从代码中将其设置为visible,进行测量,然后再设置为gone。

默认情况下它被设置为可见。 - SathMK

0

measure调用中,您不能直接使用View.MeasureSpec.*。相反,首先使用makeMeasureSpec(),然后使用它来调用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);

谢谢,但目前Kotlin限制传递此值(WRAP_CONTENT = -2 < 0)。在我的情况下,https://dev59.com/l3E85IYBdhLWcg3wVh84#53739033 在onCreateView()中有所帮助,只需调用view.measure(0, 0)。在其他事件中,您应该使用getViewTreeObserver(),请参见https://dev59.com/HmUq5IYBdhLWcg3wT-6D。 - CoolMind
1
View.MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED);not work, it says Value must be ≥ 0 (was -2) - DawnYu

0

如果有人在使用ImageView时遇到了这个问题:

我发现,如果你的ImageView没有设置"android:src"(即你动态地改变它),那么从getMeasuredWidth/Height中获取的值总是为零。

一个简单的解决方法是为视图设置一个占位符。


设置占位符后,getHeight/getMeasuredHeight仍然返回0。 - Zulqurnain Jutt

0

您可以在onCreate/onCreateView和其他事件中使用getViewTreeObserver

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()
                }
            }
        })
    }
}

并将其命名为:setGlobalLayoutListener(view) {updateUI(view)}
我也尝试过。
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)

接着在onCreateView()/onViewCreated()中调用 view.control.measuredWidth。但在一些模拟器(API 19)中返回的结果不正确。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接