Compose 布局模型允许您使用AlignmentLine
创建自定义对齐线,父布局可以使用这些对齐线来对齐和定位其子元素。例如,Row
可以使用其子元素的自定义对齐线来对齐它们。
当布局为特定的AlignmentLine
提供值时,布局的父布局可以在测量后使用相应的Placeable
实例上的Placeable.get
运算符读取此值。根据AlignmentLine
的位置,父布局可以决定子元素的位置。
Compose 中的一些可组合项已经自带对齐线。例如,BasicText
可组合项公开了FirstBaseline
和LastBaseline
对齐线。
在下面的示例中,一个名为firstBaselineToTop
的自定义LayoutModifier
读取FirstBaseline
,以从其第一条基线开始向Text
添加填充。
图 1. 显示了向元素添加普通填充与向文本元素的基线应用填充之间的区别。
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 布局中的内在测量