Линии выравнивания в Jetpack Compose

Модель макета Compose позволяет использовать AlignmentLine для создания пользовательских линий выравнивания, которые могут использоваться родительскими макетами для выравнивания и позиционирования своих дочерних элементов. Например, Row может использовать пользовательские линии выравнивания своих дочерних элементов для их выравнивания.

Когда макет предоставляет значение для конкретного AlignmentLine , родители макета могут прочитать это значение после измерения, используя оператор Placeable.get на соответствующем экземпляре Placeable . Основываясь на положении AlignmentLine , родители могут затем решить, как расположить дочерние элементы.

Некоторые компонуемые элементы в Compose уже содержат линии выравнивания. Например, компонуемый элемент BasicText выставляет линии выравнивания FirstBaseline и LastBaseline .

В примере ниже пользовательский LayoutModifier с именем firstBaselineToTop считывает 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)
        )
    }
}

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %} ,

Модель макета Compose позволяет использовать AlignmentLine для создания пользовательских линий выравнивания, которые могут использоваться родительскими макетами для выравнивания и позиционирования своих дочерних элементов. Например, Row может использовать пользовательские линии выравнивания своих дочерних элементов для их выравнивания.

Когда макет предоставляет значение для конкретного AlignmentLine , родители макета могут прочитать это значение после измерения, используя оператор Placeable.get на соответствующем экземпляре Placeable . Основываясь на положении AlignmentLine , родители могут затем решить, как расположить дочерние элементы.

Некоторые компонуемые элементы в Compose уже содержат линии выравнивания. Например, компонуемый элемент BasicText выставляет линии выравнивания FirstBaseline и LastBaseline .

В примере ниже пользовательский LayoutModifier с именем firstBaselineToTop считывает 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)
        )
    }
}

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %} ,

Модель макета Compose позволяет использовать AlignmentLine для создания пользовательских линий выравнивания, которые могут использоваться родительскими макетами для выравнивания и позиционирования своих дочерних элементов. Например, Row может использовать пользовательские линии выравнивания своих дочерних элементов для их выравнивания.

Когда макет предоставляет значение для конкретного AlignmentLine , родители макета могут прочитать это значение после измерения, используя оператор Placeable.get на соответствующем экземпляре Placeable . Основываясь на положении AlignmentLine , родители могут затем решить, как расположить дочерние элементы.

Некоторые компонуемые элементы в Compose уже содержат линии выравнивания. Например, компонуемый элемент BasicText выставляет линии выравнивания FirstBaseline и LastBaseline .

В примере ниже пользовательский LayoutModifier с именем firstBaselineToTop считывает 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)
        )
    }
}

{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %}