在 Compose 中,UI 元素由可组合函数表示,这些函数在被调用时会发出 UI 片段,然后将其添加到 UI 树中,并在屏幕上呈现。每个 UI 元素都有一个父元素和潜在的多个子元素。每个元素也位于其父元素内,指定为 (x, y) 位置,以及一个大小,指定为 width
和 height
。
父元素定义其子元素的约束。要求元素在其约束内定义其大小。约束限制元素的最小和最大 width
和 height
。如果元素有子元素,它可能会测量每个子元素以帮助确定其大小。一旦元素确定并报告其自身的大小,它就有机会定义如何相对于自身放置其子元素,如 创建自定义布局 中详细描述。
在 UI 树中布局每个节点是一个包含三个步骤的过程。每个节点必须
- 测量任何子元素
- 确定自身的大小
- 放置其子元素
作用域的使用定义了何时可以测量和放置您的子元素。测量布局只能在测量和布局阶段进行,并且子元素只能在布局阶段放置(并且仅在测量后)。由于 Compose 作用域(如 MeasureScope
和 PlacementScope
),这在编译时得到执行。
使用布局修饰符
您可以使用 layout
修饰符来修改元素的测量和布局方式。Layout
是一个 lambda;其参数包括您可以测量的元素(作为 measurable
传递)以及该可组合的传入约束(作为 constraints
传递)。自定义布局修饰符可能如下所示
fun Modifier.customLayoutModifier() = layout { measurable, constraints -> // ... }
让我们在屏幕上显示一个 Text
并控制从顶部到文本第一行基线的距离。这正是 paddingFromBaseline
修饰符的作用,我们在这里将其作为示例进行实现。为此,请使用 layout
修饰符手动将可组合项放置在屏幕上。以下是所需的 Text
顶部填充设置为 24.dp
的行为
以下是生成该间距的代码
fun Modifier.firstBaselineToTop( firstBaselineToTop: Dp ) = layout { measurable, constraints -> // Measure the composable val placeable = measurable.measure(constraints) // Check the composable has a first baseline check(placeable[FirstBaseline] != AlignmentLine.Unspecified) val firstBaseline = placeable[FirstBaseline] // Height of the composable with padding - first baseline val placeableY = firstBaselineToTop.roundToPx() - firstBaseline val height = placeable.height + placeableY layout(placeable.width, height) { // Where the composable gets placed placeable.placeRelative(0, placeableY) } }
以下是该代码中的内容
- 在
measurable
lambda 参数中,您可以通过调用measurable.measure(constraints)
来测量由 measurable 参数表示的Text
。 - 您可以通过调用
layout(width, height)
方法指定可组合项的大小,该方法还提供了一个用于放置包装元素的 lambda。在这种情况下,它是最后一个基线和添加的顶部填充之间的高度。 - 您可以通过调用
placeable.place(x, y)
在屏幕上定位包装元素。如果未放置包装元素,则它们将不可见。y
位置对应于顶部填充 - 文本第一行基线的位置。
要验证这是否按预期工作,请在 Text
上使用此修饰符
@Preview @Composable fun TextWithPaddingToBaselinePreview() { MyApplicationTheme { Text("Hi there!", Modifier.firstBaselineToTop(32.dp)) } } @Preview @Composable fun TextWithNormalPaddingPreview() { MyApplicationTheme { Text("Hi there!", Modifier.padding(top = 32.dp)) } }
创建自定义布局
layout
修饰符仅更改调用可组合项。要测量和布局多个可组合项,请改用 Layout
可组合项。此可组合项允许您手动测量和布局子元素。所有更高级别的布局(如 Column
和 Row
)都是使用 Layout
可组合项构建的。
让我们构建一个非常基本的 Column
版本。大多数自定义布局都遵循此模式
@Composable fun MyBasicColumn( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurables, constraints -> // measure and position children given constraints logic here // ... } }
与 layout
修饰符类似,measurables
是需要测量的子元素列表,constraints
是来自父元素的约束。遵循与之前相同的逻辑,MyBasicColumn
可以这样实现
@Composable fun MyBasicColumn( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( modifier = modifier, content = content ) { measurables, constraints -> // Don't constrain child views further, measure them with given constraints // List of measured children val placeables = measurables.map { measurable -> // Measure each children measurable.measure(constraints) } // Set the size of the layout as big as it can layout(constraints.maxWidth, constraints.maxHeight) { // Track the y co-ord we have placed children up to var yPosition = 0 // Place children in the parent layout placeables.forEach { placeable -> // Position item on the screen placeable.placeRelative(x = 0, y = yPosition) // Record the y co-ord placed up to yPosition += placeable.height } } } }
子可组合项受 Layout
约束(无 minHeight
约束)约束,并且根据前一个可组合项的 yPosition
进行放置。
以下是此自定义可组合项的使用方式
@Composable fun CallingComposable(modifier: Modifier = Modifier) { MyBasicColumn(modifier.padding(8.dp)) { Text("MyBasicColumn") Text("places items") Text("vertically.") Text("We've done it by hand!") } }
布局方向
通过更改 LocalLayoutDirection
组合局部来更改可组合项的布局方向。
如果您正在手动将可组合项放置在屏幕上,则 LayoutDirection
是 layout
修饰符或 Layout
可组合项的 LayoutScope
的一部分。
使用 layoutDirection
时,请使用 place
放置可组合项。与 placeRelative
方法不同,place
不会根据布局方向(从左到右与从右到左)更改。
自定义布局实战
在 Compose 中的基本布局 中了解有关布局和修饰符的更多信息,并在 创建自定义布局的 Compose 示例 中查看自定义布局的实际应用。
了解更多
要了解有关 Compose 中自定义布局的更多信息,请查阅以下其他资源。
视频
为您推荐
- 注意:当 JavaScript 关闭时,会显示链接文本
- Compose 布局中的内在测量
- Compose 中的图形
- Compose 修饰符