Compose 布局模型允许您使用 AlignmentLine
创建自定义对齐线,父布局可以使用这些对齐线来对齐和定位其子项。例如,Row
可以使用其子项的自定义对齐线来对齐它们。
当布局为特定 AlignmentLine
提供值时,布局的父级可以在测量后,使用相应的 Placeable
实例上的 Placeable.get
运算符读取此值。父级可以根据 AlignmentLine
的位置决定子项的定位。
Compose 中的某些可组合项已经附带了对齐线。例如,BasicText
可组合项公开了 FirstBaseline
和 LastBaseline
对齐线。
在下面的示例中,一个名为 firstBaselineToTop
的自定义 LayoutModifier
读取 FirstBaseline
,以便在 Text
的首行基线处添加内边距。
图 1. 显示了为元素添加常规内边距与为 Text 元素的基线应用内边距之间的区别。
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) } } @Preview @Composable private fun TextWithPaddingToBaseline() { MaterialTheme { Text("Hi there!", Modifier.firstBaselineToTop(32.dp)) } }
为了读取示例中的 FirstBaseline
,在测量阶段使用了 placeable [FirstBaseline]
。
创建自定义对齐线
创建自定义 Layout
可组合项或自定义 LayoutModifier
时,您可以提供自定义对齐线,以便其他父级可组合项可以使用它们来相应地对齐和定位其子项。
以下示例显示了一个自定义 BarChart
可组合项,它公开了两个对齐线:MaxChartValue
和 MinChartValue
,以便其他可组合项可以对齐到图表的最小和最大数据值。两个文本元素 Max 和 Min 已与自定义对齐线的中心对齐。
图 2. BarChart
可组合项,其中文本与最大和最小数据值对齐。
自定义对齐线在您的项目中定义为顶级变量。
/** * AlignmentLine defined by the maximum data value in a [BarChart] */ private val MaxChartValue = HorizontalAlignmentLine(merger = { old, new -> min(old, new) }) /** * AlignmentLine defined by the minimum data value in a [BarChart] */ private val MinChartValue = HorizontalAlignmentLine(merger = { old, new -> max(old, new) })
用于创建示例的自定义对齐线属于 HorizontalAlignmentLine
类型,因为它们用于垂直对齐子项。如果多个布局为这些对齐线提供值,则会传入合并策略作为参数。由于 Compose 布局系统坐标和 Canvas
坐标表示 [0, 0]
(即左上角,x
轴和 y
轴正方向向下),因此 MaxChartValue
值将始终小于 MinChartValue
。因此,图表数据最大值基线的合并策略为 min
,图表数据最小值基线的合并策略为 max
。
创建自定义 Layout
或 LayoutModifier
时,请在 MeasureScope.layout
方法中指定自定义对齐线,该方法接受一个 alignmentLines: Map<AlignmentLine, Int>
参数。
@Composable private fun BarChart( dataPoints: List<Int>, modifier: Modifier = Modifier, ) { val maxValue: Float = remember(dataPoints) { dataPoints.maxOrNull()!! * 1.2f } BoxWithConstraints(modifier = modifier) { val density = LocalDensity.current with(density) { // ... // Calculate baselines val maxYBaseline = // ... val minYBaseline = // ... Layout( content = {}, modifier = Modifier.drawBehind { // ... } ) { _, constraints -> with(constraints) { layout( width = if (hasBoundedWidth) maxWidth else minWidth, height = if (hasBoundedHeight) maxHeight else minHeight, // Custom AlignmentLines are set here. These are propagated // to direct and indirect parent composables. alignmentLines = mapOf( MinChartValue to minYBaseline.roundToInt(), MaxChartValue to maxYBaseline.roundToInt() ) ) {} } } } } }
此可组合项的直接和间接父级可以使用对齐线。以下可组合项创建了一个自定义布局,它接受两个 Text
插槽和数据点作为参数,并将两个文本对齐到图表的最小和最大数据值。此可组合项的预览如图 2 所示。
@Composable private fun BarChartMinMax( dataPoints: List<Int>, maxText: @Composable () -> Unit, minText: @Composable () -> Unit, modifier: Modifier = Modifier, ) { Layout( content = { maxText() minText() // Set a fixed size to make the example easier to follow BarChart(dataPoints, Modifier.size(200.dp)) }, modifier = modifier ) { measurables, constraints -> check(measurables.size == 3) val placeables = measurables.map { it.measure(constraints.copy(minWidth = 0, minHeight = 0)) } val maxTextPlaceable = placeables[0] val minTextPlaceable = placeables[1] val barChartPlaceable = placeables[2] // Obtain the alignment lines from BarChart to position the Text val minValueBaseline = barChartPlaceable[MinChartValue] val maxValueBaseline = barChartPlaceable[MaxChartValue] layout(constraints.maxWidth, constraints.maxHeight) { maxTextPlaceable.placeRelative( x = 0, y = maxValueBaseline - (maxTextPlaceable.height / 2) ) minTextPlaceable.placeRelative( x = 0, y = minValueBaseline - (minTextPlaceable.height / 2) ) barChartPlaceable.placeRelative( x = max(maxTextPlaceable.width, minTextPlaceable.width) + 20, y = 0 ) } } } @Preview @Composable private fun ChartDataPreview() { MaterialTheme { BarChartMinMax( dataPoints = listOf(4, 24, 15), maxText = { Text("Max") }, minText = { Text("Min") }, modifier = Modifier.padding(24.dp) ) } }
推荐阅读
- 注意:禁用 JavaScript 时会显示链接文本
- Compose 中的图形
- 自定义布局 {:#custom-layouts }
- Compose 布局中的内在测量