Jetpack Compose'da hizalama çizgileri

Oluştur düzen modeli, AlignmentLine ile özel öğeler oluşturmanızı sağlar üst düzenleri hizalayıp konumlandırmak için kullanabileceğiniz hizalama çizgileridir. Örneğin, Row hizalamak için alt öğelerin özel hizalama çizgilerini kullanabilir.

Bir düzen belirli bir AlignmentLine için değer sağladığında, düzenin ebeveynler, Placeable.get kullanarak ölçümden sonra bu değeri okuyabilir operatörümüzün Placeable örneği. AlignmentLine adlı cihazın konumuna bağlı olarak ebeveynler şunları yapabilir: ardından çocukların yerine nasıl karar verilir?

Oluşturma bölümündeki bazı bileşenler zaten hizalama çizgileriyle birlikte gelir. Örneğin, BasicText birleşik öğesi FirstBaseline ve LastBaseline hizalama çizgilerini gösterir.

Aşağıdaki örnekte, özel bir LayoutModifier firstBaselineToTop, Text öğesine dolgu eklemek için FirstBaseline öğesini okur başlangıçtaki temel değerden başlayarak.

Şekil 1. Bir öğeye normal dolgu ekleme arasındaki farkı gösterir. ve bir Metin öğesinin temel çizgisine dolgu uygulanır.

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

Örnekte FirstBaseline değerini okumak için ölçüm aşamasında placeable [FirstBaseline] kullanılır.

Özel hizalama çizgileri oluşturma

Özel bir Layout oluştururken doğrudan composable veya özel bir LayoutModifier diğer üst composable'ların bunları hizalamak için kullanabilmesi için özel hizalama satırları uygun şekilde konumlandırmaktır.

Aşağıdaki örnekte iki farklı öğe oluşturan özel bir BarChart composable, MaxChartValue ve MinChartValue hizalama çizgilerini gösterir. Böylece diğer composable'lar aynı olmalıdır. Maks ve Min adlı iki metin öğesi, özel hizalama çizgilerinin ortasına hizalanmıştır.

Şekil 2. BarChart, maksimum ve minimum veri değerine hizalanmış metinle birleştirilebilir.

Özel hizalama çizgileri projenizdeki üst düzey değişkenler olarak tanımlanır.

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

Örneğimizi oluşturmak için kullandığımız özel hizalama çizgileri, alt öğeleri dikey olarak hizalamak için kullanıldığı için HorizontalAlignmentLine türündedir. Birleştirme politikası parametresini kullanabilirsiniz. Oluşturma düzeni sistem koordinatları ve Canvas koordinatları [0, 0]'ı temsil ettiğinden, sol üst köşe ve x ile y ekseni aşağı doğru pozitif olduğundan MaxChartValue değeri her zaman MinChartValue'ten daha küçük olacaktır. Bu nedenle birleşme politikası, maksimum grafik için min veri değeri referans değeri ve minimum grafik veri değeri temel çizgisi için max.

Özel Layout veya LayoutModifier oluştururken özel hizalamayı belirtin MeasureScope.layout içindeki satırlar alignmentLines: Map<AlignmentLine, Int> gerekir. parametresinden sonra bir değer girin.

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

Bu composable'ın doğrudan ve dolaylı üst öğeleri, hizalamayı tüketebilir çizgi şeklinde görünür. Aşağıdaki composable'ın oluşturduğu özel bir düzen parametre iki Text alanlarını ve veri noktalarını kullanır ve iki metni reklam ile hizalar hem maksimum hem de minimum grafik verisi değerleridir. Bu bileşiğin önizlemesi Şekil 2'de gösterilmektedir.

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