为什么LinearLayout不按重量比例分配收缩?

时间:2017-12-19 19:32:35

标签: android android-layout android-linearlayout

我的理解是LinearLayout应该分配松弛 (额外空间)和/或收缩(负额外空间) 根据孩子的布局重量。 这看起来像我期望的那样松弛,但不是缩小。

以下android活动演示。 它显示了9列(垂直LinearLayouts),具有不同的度数 拥挤。 9列中的每一列都有两个孩子:

  • 第一个孩子是一个子列,由3个标有“A”,“B”,“C”的单选按钮组成
  • 第二个孩子是一个由2个标有“0”,“1”
  • 的单选按钮组成的子列

每个子列的权重与单选按钮的数量成正比 在它(3或2)中,子列中每个单选按钮的权重为1。

我选择了这些权重以分配列的额外空间 (孙女)(孙女按钮)平等(负面或正面), 这样,给定列中的所有5个单选按钮的大小都相同。

它看似按预期用于松弛(红色列,在右侧), 但不是缩小(蓝色列,左侧),如下所示 屏幕截图显示(启用了开发人员选项“显示布局边界”)。 在最拥挤(最左边)的列中,差异最明显, 其中前三个单选按钮最终比它们的两个堂兄小得多。

[screenshot]

这是LinearLayout的预期行为吗? 或者它是LinearLayout中的错误?或者我的程序中有错误?

这是程序列表(在Kotlin中):

// app/src/main/java/com/example/donhatch/linearlayoutweightsquestionactivity/MainActivity.kt
// Simple activity to test LinearLayout's slack/shrinkage distribution behavior.
package com.example.donhatch.linearlayoutweightsquestionactivity

import android.graphics.Color
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.RadioGroup

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val c = applicationContext
    val WC = LinearLayout.LayoutParams.WRAP_CONTENT
    setContentView(object: LinearLayout(c) {init{
      // A row of 9 columns, from crowded to uncrowded.
      orientation = HORIZONTAL
      val columnHeights = arrayOf(
        200, 300, 400, 500,  // crowded
        WC,  // just right (comes out 5*112 = 560)
        600, 700, 800, 900  // uncrowded
      )
      for (columnHeight in columnHeights) {
        addView(object: LinearLayout(c) {init{
          // A column with two child columns (radio groups).
          orientation = VERTICAL
          setBackgroundColor(when {
            columnHeight==WC -> Color.WHITE
            columnHeight<560 -> Color.argb(255,224,224,255) // crowded: blue
            else             -> Color.argb(255,255,224,224) // uncrowded: red
          })
          addView(object: RadioGroup(c) {init{
            // First child column: three radio buttons.
            orientation = VERTICAL
            for (i in 0 until 3) {
              addView(object: RadioButton(c) {init{
                text = 'A'.plus(i) + " "  // "A", "B", ...
              }}, LayoutParams(WC, WC, 1.0f))
            }
          }}, LayoutParams(WC, WC, 3.0f))  // weight=3 for 3 children
          addView(object: RadioGroup(c) {init{
            // Second child column: two radio buttons.
            orientation = VERTICAL
            for (i in 0 until 2) {
              addView(object: RadioButton(c) {init{
                text = '0'.plus(i) + " "  // "0", "1", ...
              }}, LayoutParams(WC, WC, 1.0f))
            }
          }}, LayoutParams(WC, WC, 2.0f))  // weight=2 for 2 children
        }}, LayoutParams(WC, columnHeight))
      }  // for columnHeight
    }})  // setContentView
  }  // onCreate
}  // class MainActivity

1 个答案:

答案 0 :(得分:1)

我跟踪了LinearLayouts和RadioButtons中对onMeasure的调用, 我相信我明白现在发生了什么。基于此,我相信这是LinearLayout中的一个错误。

它出现了什么

关注高度为300的列,其中有3 + 2个单选按钮,以下是计算结果。

使用onMeasure()调用父列的heightSpec = EXACTLY 300。 它问两个孩子他们想要多大,被限制为只不过是自己的 确切知道身高300:

  • 使用onMeasure()调用第一个子列的heightSpec = AT_MOST 300。 第一个子列的总首选高度为336 = 3 * 112(3个单选按钮,每个按钮高度为112像素)超出预算, 所以它回答它希望它的高度是允许的最大值,即300。
  • 使用onMeasure()调用第二个子列的heightSpec = AT_MOST 300。 第二个子列的总首选高度为224 = 2 * 112(2个单选按钮),这在预算范围内, 所以它回复说它希望有高度224。

因此,孩子们报告的总首选大小是300 + 224 = 524,这个大小超过224像素。 因此,它将收缩224分配给两个孩子,其比例为3:2; 也就是说,第一个孩子收缩(3/5)* 224 = 134.4(四舍五入到134)和 第二个孩子收缩(2/5)* 224 = 89.6(四舍五入到90)。 因此,两个子列分别达到300-134 = 166和224-90 = 134的高度。 这两个子栏目现在告诉他们的高度,让他们分发给他们的孩子:

  • 使用onMeasure()调用第一个孩子的heightSpec="EXACTLY 166"。 它将这个高度166公平分配给它的三个孩子,使它们的高度为56,55,55。
  • 使用onMeasure()调用第二个孩子的heightSpec="EXACTLY 134"。 它将高度134分配给它的两个孩子,使它们的高度达到67,67。

因此,列中5个单选按钮的最终高度为56,55,55,67,67; 即前三个小于后两个。这解释了不公平的分配。

什么错了

在我看来,在向孩子们询问什么样的高度时,错误是指定AT_MOST 300 他们希望:MeasureSpec模式AT_MOST既不需要也不适合, 并且它导致收缩的不公平分布。 MeasureSpec模式UNSPECIFIED 本来是一个更好的选择:它最终会在没有任何限制的情况下得出正确的答案 违规。

它应该做什么

让我们再次进行计算,但使用UNSPECIFIED代替AT_MOST 300 当问孩子们他们想要什么样的高度时。

使用onMeasure()调用父列的heightSpec = EXACTLY 300。 它问两个孩子他们有多大,没有任何限制:

  • 使用onMeasure()调用第一个子列的heightSpec = UNSPECIFIED。 第一个子列回复它想要高度336 = 3 * 112(3个单选按钮)。
  • 使用onMeasure()调用第二个子列的heightSpec = UNSPECIFIED 第二个子列回复它希望高度224 = 2 * 112(2个单选按钮)。

所以孩子们的首选大小是336 + 224 = 560,这个数字太大了260像素。 因此,它将收缩率260分配给两个孩子,其比例为3:2; 也就是说,第一个孩子收缩(3/5)* 260 = 156和 第二个孩子收缩(2/5)* 260 = 104。 因此,两个子列分别达到336-156 = 180和224-104 = 120的高度。 这两个子栏目现在告诉他们的高度,并告诉他们的后代:

  • 使用onMeasure()调用第一个孩子的heightSpec = EXACTLY 180。 它将高度180分配给它的三个孩子,使它们的高度达到60,60,60。
  • 使用onMeasure()调用第二个孩子的heightSpec = EXACTLY 120 它将高度120公平地分配给它的两个孩子,使它们的高度为60,60。

因此,列中5个单选按钮的最终高度为60,60,60,60,60, 这是理想的行为。