Модель макета Compose позволяет использовать AlignmentLine для создания специальных линий выравнивания, которые могут использоваться родительскими макетами для выравнивания и позиционирования дочерних элементов. Например, Row может использовать специальные линии выравнивания дочерних элементов, чтобы выровнять их.
Если макет задает значение для определенного AlignmentLine, родительские элементы макета могут прочитать это значение после измерения, используя оператор Placeable.get для соответствующего экземпляра Placeable. Исходя из положения AlignmentLine, родители могут решить, как расположить детей.
Некоторые компоненты 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-функции могли использовать их для выравнивания и позиционирования своих дочерних элементов.
В примере ниже показан пользовательский компонент BarChart, который предоставляет две линии выравнивания: MaxChartValue и MinChartValue. Благодаря этому другие компоненты могут выравниваться по максимальному и минимальному значению данных на диаграмме. Два текстовых элемента, Max и Min, выровнены по центру пользовательских линий выравнивания.
BarChartcomposable-функция с текстом, выровненным по максимальному и минимальному значению данных.Пользовательские линии выравнивания определяются как переменные верхнего уровня в проекте.
/** * 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 отключен.
- Графика в режиме создания письма
- Собственные макеты {:#custom-layouts }
- Встроенные измерения в макетах Compose