Ausrichtungslinien in Jetpack Compose

Mit dem Layoutmodell „Schreiben“ können Sie mit AlignmentLine benutzerdefinierte Ausrichtungslinien, die von übergeordneten Layouts verwendet werden können, um ihre Kinder. Beispiel: Row können die benutzerdefinierten Ausrichtungslinien der untergeordneten Elemente verwenden, um sie auszurichten.

Wenn ein Layout einen Wert für eine bestimmte AlignmentLine bereitstellt, Eltern können diesen Wert nach der Messung über die Placeable.get abrufen. im entsprechenden Placeable-Instanz. Basierend auf der Position von AlignmentLine können die übergeordneten Elemente und legt dann die Positionierung der untergeordneten Elemente fest.

Einige zusammensetzbare Funktionen in „Schreiben“ sind bereits mit Ausrichtungslinien ausgestattet. Beispiel: Der Parameter BasicText zusammensetzbare Funktion macht die Ausrichtungslinien FirstBaseline und LastBaseline sichtbar.

Im Beispiel unten hat ein benutzerdefiniertes LayoutModifier mit dem Namen firstBaselineToTop liest die FirstBaseline, um der Text einen Abstand hinzuzufügen beginnend bei der ersten Baseline.

Abbildung 1: Zeigt den Unterschied zwischen dem Hinzufügen eines normalen Abstands zu einem Element, und das Auffüllen der Referenz eines Textelements.

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))
    }
}

Um die FirstBaseline in diesem Beispiel zu lesen, placeable [FirstBaseline] wird in der Messphase verwendet.

Benutzerdefinierte Ausrichtungslinien erstellen

Beim Erstellen eines benutzerdefinierten Layout zusammensetzbaren oder benutzerdefinierten LayoutModifier verwenden, können Sie benutzerdefinierte Ausrichtungslinien, damit sie von anderen übergeordneten zusammensetzbaren Funktionen verwendet werden können, und ihre Kinder entsprechend zu positionieren.

Das folgende Beispiel zeigt eine benutzerdefinierte zusammensetzbare Funktion BarChart, die zwei zusammensetzbare Funktionen Ausrichtungslinien, MaxChartValue und MinChartValue, sodass andere zusammensetzbare Funktionen am Höchst- und Mindestdatenwert des Diagramms ausgerichtet werden. Zwei Text Max und Min, wurden an der Mitte des benutzerdefinierten Ausrichtungslinien.

Abbildung 2: BarChart zusammensetzbar, wobei der Text am Maximalwert und Minimaler Datenwert.

Benutzerdefinierte Ausrichtungslinien werden als Variablen der obersten Ebene in Ihrem Projekt definiert.

/**
 * 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)
})

Die benutzerdefinierten Ausrichtungslinien, um unser Beispiel zu erstellen, sind vom Typ HorizontalAlignmentLine, wie werden die untergeordneten Elemente vertikal ausgerichtet. Eine Zusammenführungsrichtlinie wird übergeben als -Parameter, falls mehrere Layouts einen Wert für diese Ausrichtungslinien bereitstellen. Als die Koordinaten des Layoutsystems „Compose“ und die Canvas Koordinaten stellen [0, 0] dar, die obere linke Ecke sowie die Achse x und y sind positiv nach unten, sodass der Wert MaxChartValue immer kleiner ist als MinChartValue Daher lautet die Fusionsrichtlinie für das höchste Diagramm „min“. Datenwert-Baseline und max für die minimale Referenz der Diagrammdatenwerte.

Geben Sie beim Erstellen eines benutzerdefinierten Layout- oder LayoutModifier-Elements eine benutzerdefinierte Ausrichtung an. Zeilen in der MeasureScope.layout mit einem alignmentLines: Map<AlignmentLine, Int>-Wert .

@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()
                        )
                    ) {}
                }
            }
        }
    }
}

Direkte und indirekte übergeordnete Elemente dieser zusammensetzbaren Funktion können die Ausrichtung verarbeiten. Linien. Die folgende zusammensetzbare Funktion erstellt ein benutzerdefiniertes Layout, das als Parameter zwei Text-Flächen und Datenpunkte und richtet die beiden Texte an den maximale und minimale Diagrammdatenwerte. Die Vorschau dieser zusammensetzbaren Funktion ist was in Abbildung 2 dargestellt ist.

@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)
        )
    }
}