onMeasure()调用EXACTLY,规格大小为0

时间:2014-03-29 05:25:44

标签: android android-layout android-custom-view

在调试自定义视图覆盖onMeasure()方法时,我发现有几次调用此方法 我只处理视图的高度,保持宽度规格始终不变 在某些时候,我接到一个叫(高度)MeasureSpec getMode() == EXACTLYgetSize() == 0的电话。 没有意义与Android文档相矛盾:

MeasureSpecs are used to push requirements down the tree from parent to child.
A MeasureSpec can be in one of three modes:

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.

EXACTLY: This is used by the parent to impose an exact size on the child. The child
must use this size, and guarantee that all of its descendants will fit within this 
size.

AT_MOST: This is used by the parent to impose a maximum size on the child. The child
must guarantee that it and all of its descendants will fit within this size.

如果我这样做(孩子必须使用此尺寸),setMeasureDimension(specWidth, sepcHeight)我得到一个例外,告诉视图的宽度和高度必须是> 0
我怀疑这个调​​用是因为在布局XML中,视图有layout_weight="1",并且根据the documentation suggestions

  

创建一个线性布局,其中每个孩子使用相同数量的   屏幕上的空格,设置每个视图的android:layout_height   " 0dp"

但是,当MeasureSpec模式完全正确时,尺寸应该> 0.或者至少在这些情况下应该遵循一些规则,在文档中。

这是代码:

    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int specHeight = MeasureSpec.getSize(heightMeasureSpec);
    int specWidth = MeasureSpec.getSize(widthMeasureSpec);

    int desiredHeight = Math.max(BOX_MIN_HEIGHT, HSVColorPickerPreference.this.boxHeight);

    int chosenHeight = 0;

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if( heightMode == MeasureSpec.UNSPECIFIED ) {
        chosenHeight = desiredHeight;
    } else if( heightMode == MeasureSpec.AT_MOST ) {
        chosenHeight = Math.min(specHeight, desiredHeight);
    } else if( heightMode == MeasureSpec.EXACTLY ) {
        chosenHeight = specHeight;
    }

    setMeasuredDimension(specWidth, chosenHeight);

这是日志,请注意最后一次调用onMeasure()

03-29 08:17:13.388: D/ValueSlider(1384): + onMeasure(widthMeasureSpec:1073742403, heightMeasureSpec:-2147483435)
03-29 08:17:13.388: W/ValueSlider(1384): MeasureSpec AT_MOST, specSize=213, desiredSize=40, chosenSize=40
03-29 08:17:13.388: D/AlphaSlider(1384): + onMeasure(widthMeasureSpec:1073742403, heightMeasureSpec:-2147483435)
03-29 08:17:13.388: W/AlphaSlider(1384): MeasureSpec AT_MOST, specSize=213, desiredSize=40, chosenSize=40
03-29 08:17:13.388: D/ValueSlider(1384): + onMeasure(widthMeasureSpec:1073742403, heightMeasureSpec:1073741824)
03-29 08:17:13.388: W/ValueSlider(1384): MeasureSpec EXACTLY, specSize=0, desiredSize=40, chosenSize=0
03-29 08:17:13.388: D/AlphaSlider(1384): + onMeasure(widthMeasureSpec:1073742403, heightMeasureSpec:1073741824)
03-29 08:17:13.388: W/AlphaSlider(1384): MeasureSpec EXACTLY, specSize=0, desiredSize=40, chosenSize=0
03-29 08:17:13.508: D/ValueSlider(1384): + onSizeChanged(w:579, h:0, oldw:0, oldh:0)
03-29 08:17:13.508: D/AndroidRuntime(1384): Shutting down VM
03-29 08:17:13.508: W/dalvikvm(1384): threadid=1: thread exiting with uncaught exception (group=0xb2fe0180)
03-29 08:17:13.518: E/AndroidRuntime(1384): FATAL EXCEPTION: main
03-29 08:17:13.518: E/AndroidRuntime(1384): java.lang.IllegalArgumentException: width and height must be > 0
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.graphics.Bitmap.createBitmap(Bitmap.java:603)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.graphics.Bitmap.createBitmap(Bitmap.java:585)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at com.UturpatShuPepper.lib.HSVColorPickerPreference$Slider.onSizeChanged(HSVColorPickerPreference.java:962)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.setFrame(View.java:11361)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11272)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.RelativeLayout.onLayout(RelativeLayout.java:925)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1628)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1486)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.LinearLayout.onLayout(LinearLayout.java:1399)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.widget.FrameLayout.onLayout(FrameLayout.java:431)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.View.layout(View.java:11278)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewGroup.layout(ViewGroup.java:4224)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1489)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2442)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.os.Handler.dispatchMessage(Handler.java:99)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.os.Looper.loop(Looper.java:137)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at android.app.ActivityThread.main(ActivityThread.java:4424)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at java.lang.reflect.Method.invokeNative(Native Method)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at java.lang.reflect.Method.invoke(Method.java:511)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
03-29 08:17:13.518: E/AndroidRuntime(1384):     at dalvik.system.NativeStart.main(Native Method)

1 个答案:

答案 0 :(得分:5)

由于我终于理解了发生了什么,我将在此留下答案以供将来参考:

Android在测量UI组件时正在做它必须做的事情 如果用户(在这种情况下我)不遵循简单规则,则可能会发生完全0 如果您只是在onSizeChanged()方法中检查0大小,它可以变得无害。但是,如果你避免混合测量模式,就像我做的那样更好。说明如下。

我在XML加权视图中定义(使用layout_weight)。这些是问题中提到的自定义视图。我的错误是尝试在

中要求特定的视图高度
@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)

罪魁祸首是行

int desiredHeight = Math.max(BOX_MIN_HEIGHT, HSVColorPickerPreference.this.boxHeight);

. . . 

    chosenHeight = Math.min(specHeight, desiredHeight);

. . .

这与加权布局的启发式发生冲突。为什么?让我们举例说明权重为1的3个小部件,其中一个小部件表现不好,如上所述。

当LineraLayout首次通过其子项时,它会让它们变得疯狂并要求任何他们想要的大小。在我们的示例中,2个小部件将尽可能地要求,自定义小部件将要求适度的,小于最大值。

第二遍是杀手,LinearLayout不知道其中一个加权小部件要求的数量少于预期,总而言之,它有一个为它定义的权重。 LinearLayout查看传递的总请求度量表单,并且看到它比它必须给出的更多。然后它计算delta溢出并进行另一次传递,在加权小部件之间分配溢出。 因此,自定义视图小部件必须减少超过其请求的数量,使其保持0大小。

情况类似于和朋友一起喝啤酒。你点一杯啤酒,你的朋友点啤酒,薯条,追逐者,这个地段。在晚上结束时,支票在每个人之间平均分配,最终支付的费用超过了你的啤酒。我的自定义视图也是如此。