Модель макета Compose позволяет использовать AlignmentLine для создания пользовательских линий выравнивания, которые могут использоваться родительскими макетами для выравнивания и позиционирования своих дочерних элементов. Например, Row может использовать специальные линии выравнивания дочерних элементов, чтобы выровнять их.
Когда макет предоставляет значение для определенного AlignmentLine, родительские элементы макета могут прочитать это значение после измерения, используя оператор Placeable.get в соответствующем экземпляре Placeable. В зависимости от положения AlignmentLine родители могут решить, как расположить дочерние элементы.
Некоторые composable-функции в Compose уже поставляются с линиями выравнивания. Например, composable-функция BasicText предоставляет линии выравнивания FirstBaseline и LastBaseline.
В примере ниже модификатор LayoutModifier с названием firstBaselineToTop считывает FirstBaseline, чтобы добавить отступ к 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].
Как создать собственные направляющие
При создании пользовательской composable-функции Layout или пользовательского LayoutModifier вы можете указать пользовательские линии выравнивания, чтобы другие родительские composable-функции могли использовать их для выравнивания и позиционирования своих дочерних элементов.
В примере ниже показана пользовательская composable-функция BarChart, которая предоставляет две линии выравнивания, MaxChartValue и MinChartValue, чтобы другие composable-функции могли выравниваться по максимальному и минимальному значению данных диаграммы. Два текстовых элемента, Max и Min, выровнены по центру пользовательских линий выравнивания.
BarChart composable-функция с текстом, выровненным по максимальному и минимальному значениям данных.Пользовательские линии выравнивания определяются как переменные верхнего уровня в проекте.
/** * 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() ) ) {} } } } } }
Прямые и косвенные родительские элементы этой composable-функции могут использовать линии выравнивания. Следующая composable-функция создает собственный макет, который принимает в качестве параметра два слота Text и элементы данных и выравнивает два текста по максимальному и минимальному значениям данных диаграммы. Предварительный просмотр этой composable-функции показан на рисунке 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