自定义布局忽略可组合项的大小

Custom layout ignores size of composeable

我目前正在研究 Jetpack Compose 中的自定义布局。这是我到目前为止仅用于测试的结果:

@Preview(widthDp = 1000, heightDp = 1000)
@Composable
fun MyLayout() {
    Layout({ Box(Modifier.size(48.dp).background(Color.Blue)) }) { measurables, constraints ->
        val placeables = measurables.map { it.measure(constraints) }
        layout(constraints.maxWidth, constraints.maxHeight) {
            placeables.forEach { it.place(0, 0) }
        }
    }
}

我的问题是 Box 填满了整个布局,而不仅仅是 48.dp 的大小。有人可以向我解释这是为什么吗?我阅读了 here 关于创建自定义布局的内容,但找不到任何有用的内容。

Layout 大小由传入 layout(width, height) 的大小决定。

您的 Layout 没有任何修饰符,这就是为什么它具有等于可用间距的 maxWidth/maxHeight 约束。使用 layout(constraints.maxWidth, constraints.maxHeight) 与应用 Modifier.fillMaxSize 具有相同的效果。实际上,使用 Modifier.fillMaxSize,您会将最大和最小限制都设置为可用大小。

要解决这个问题,您有两个选择:

  1. Modifier.size 应用于 Layout 本身,而不是应用于 Box - 在这种情况下 max/min 约束将是 48.dp

  2. 根据 placeable 计算布局大小。实际代码取决于您希望获得的布局。您的代码看起来像 Box 模拟,因此您需要所有可放置大小中的最大值:

    layout(placeables.maxOfOrNull { it.width } ?: 0, placeables.maxOfOrNull { it.height } ?: 0) {
    

蓝色框填满整个 Layout 的原因是由于以下几点的结合:(1) 您为 @Preview 注释指定了尺寸,(2) 您使用Layout 的约束未修改以测量框,以及 (3) 使用 size 修饰符。

由于第 1 点,Layout 的约束将最小值和最大值都设置为预览尺寸中指定的值。由于第 2 点,这些严格的最小值和最大值用于测量框。由于第 3 点,传入的约束优先于请求的大小(这就是 size 修饰符的工作方式)。

缓解其中的每一点都将解决您的问题。

正如您已经从其他答案之一中发现的那样,从预览注释中删除宽度和高度将使它们看起来正确。这是因为如果没有在 Preview 注释中指定的宽度和高度,约束将具有最小值 0,因此现在允许可测量的子项采用他们想要的大小。

不过,推荐的方法是修复第 2 点:在将约束传递给可测量对象的测量方法之前更改约束以适应自定义布局的要求。在这种情况下,您可能希望创建约束的副本并将最小宽度和高度设置为 0。

最后,您可以改用 requiredSize 来修正第 3 点,这会将可测量对象的大小调整到所需的大小,尽管它仍会向其父级传达一个在限制范围内的大小。这通常不建议在生产代码中使用,因为它使大小调整变得绝对,但对于预览来说它应该没问题,对于测试它甚至可能是可取的。