【问题标题】:Why isn't LinearLayout distributing shrinkage in proportion to weights?为什么 LinearLayout 不按权重比例分配收缩?
【发布时间】:2017-12-19 19:32:35
【问题描述】:

我的理解是 LinearLayout 应该分配 slack (额外空间)和/或收缩(负额外空间) 根据其孩子的布局权重。 这似乎适用于我对松弛的预期,但不适用于收缩。

以下 android Activity 演示。 它显示 9 列(垂直线性布局),不同程度 的拥挤。 9 列中的每一列都有两个子项:

  • 第一个子列是一个子列,由标记为“A”、“B”、“C”的 3 个单选按钮组成
  • second child 是一个子列,由标记为“0”、“1”的 2 个单选按钮组成

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

我选择这些权重是为了分配列的额外空间 (负或正)在其孙子(单选按钮)中均等, 以便给定列中的所有 5 个单选按钮最终大小相同。

它似乎可以正常工作(右侧的红色列), 但不适用于收缩(左侧的蓝色列),如下所示 屏幕截图显示(启用开发人员选项“显示布局边界”)。 在最拥挤(最左边)的列中差异最为明显, 其中前 3 个单选按钮最终比它们的 2 个表亲小得多。

这是 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

【问题讨论】:

    标签: android android-layout android-linearlayout


    【解决方案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 像素太大。 因此它以 3:2 的比例将收缩 224 分配给两个孩子; 也就是说,第一个孩子的收缩率为 (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 像素来说太大了。 因此它以 3:2 的比例将收缩 260 分配给两个孩子; 也就是说,第一个孩子得到收缩 (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, 这是期望的行为。

    【讨论】:

    • 这很有趣,我很怀疑,但最后我认为我同意了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-20
    • 2021-07-02
    • 2010-12-27
    相关资源
    最近更新 更多