OnMeasure (), вызванное ТОЧНО и размер спецификации 0

При отладке настраиваемого представления override onMeasure() я вижу, что есть несколько вызовов этого метода.
Я имею дело только с высотой представления, оставляя спецификацию ширины неизменной.
В какой-то момент я получаю вызов с (высотой) MeasureSpec getMode() == EXACTLY и getSize() == 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) Я получаю исключение setMeasureDimension(specWidth, sepcHeight) что ширина и высота представления должны быть> 0.
Я подозреваю, что этот вызов вызван тем, что в XML-макете представление имеет layout_weight="1" и, согласно предложениям документации :

Чтобы создать линейный макет, в котором каждый ребенок использует такое же пространство на экране, установите для 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) 

Поскольку я наконец понял, что происходит, я оставляю здесь ответ для дальнейшего использования:

Android делает то, что он должен делать при измерении компонентов пользовательского интерфейса.
Если пользователь (я в этом случае) не соблюдает простые правила, то ТОЧНО 0 может случиться.
Это можно сделать безвредным, если вы просто проверяете размер 0 в onSizeChanged() . Но даже лучше, если вы избегаете смешивания режимов измерения, как я. Пояснение следует.

Я определил в взвешенных в 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); . . . 

Это сталкивается с головой с эвристикой для взвешенной компоновки. Зачем? Давайте возьмем, например, 3 виджета с весом = 1, и один из них ведет себя плохо, как описано выше.

Когда LineraLayout делает первый проход над своими детьми, он позволяет им разозлиться и спросить любого размера, который они хотят. В нашем примере 2 виджета будут запрашивать как можно больше, пользовательский виджет будет запрашивать что-то скромное, меньше максимального.

Второй проход – это убийца, LinearLayout не знает, что один из взвешенных виджетах требует меньше, чем предполагается, и в целом он имеет определенный для него вес. LinearLayout смотрит на общую запрашиваемую форму измерения, передавая один, и видит, что это больше, чем нужно. Затем он вычисляет переполнение дельты и делает другой проход распределением переполнения среди взвешенных виджетов. Следовательно, пользовательский виджет вида должен сокращать больше, чем он запрашивал, оставляя его на уровне 0.

Ситуация является аналогом для пива с друзьями. Вы заказываете одно пиво, а ваши друзья заказывают пиво, картофель фри, охотники, лот. В конце вечера чек делится поровну между всеми, вы в конечном итоге платите больше, чем пиво у вас было. Так же сделал мой пользовательский вид.

Intereting Posts