Jetpack Compose'da hizalama çizgileri

Oluştur düzen modeli, üst düzenler tarafından alt öğelerini hizalamak ve konumlandırmak üzere kullanılabilecek özel hizalama çizgileri oluşturmak için AlignmentLine kullanmanıza olanak tanır. Örneğin Row, alt öğeleri hizalamak için alt öğeleri için özel hizalama çizgilerini kullanabilir.

Düzen belirli bir AlignmentLine için değer sağladığında düzenin üst öğeleri, ölçümden sonra karşılık gelen Placeable örneğinde Placeable.get operatörünü kullanarak bu değeri okuyabilir. Çocukların konumuna göre ebeveynler, AlignmentLine konumuna göre bu kararı verebilir.

Compose'daki bazı composable'lar zaten hizalama çizgileriyle gelir. Örneğin, BasicTextcomposable, FirstBaseline ve LastBaseline hizalama çizgilerini ortaya çıkarır.

Aşağıdaki örnekte, firstBaselineToTop adlı özel LayoutModifier, ilk temel değerinden itibaren Text öğesine dolgu eklemek için FirstBaseline değerini okumaktadır.

Şekil 1. Bir öğeye normal dolgu ekleme ile Metin öğesinin taban çizgisine dolgu uygulama arasındaki farkı gösterir.

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

Örnekteki FirstBaseline öğesini okumak için ölçüm aşamasında placeable [FirstBaseline] kullanılmıştır.

Özel hizalama çizgileri oluşturma

Özel Layoutcomposable veya özel LayoutModifier oluştururken diğer üst composable'lar tarafından alt öğelerini uygun şekilde hizalayıp konumlandırmak için özel hizalama çizgileri belirleyebilirsiniz.

Aşağıdaki örnekte, diğer composable'lar grafiğin maksimum ve minimum veri değerine hizalanabilmesi için iki hizalama çizgisini (MaxChartValue ve MinChartValue) ortaya çıkaran özel bir BarChart composable'ı gösterilmektedir. Maks ve Min olmak üzere iki metin öğesi, özel hizalama çizgilerinin ortasına hizalanmıştır.

2. Şekil. Maksimum ve minimum veri değerine hizalanan Metin ile BarChartcomposable.

Ö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 kullanılan özel hizalama çizgileri, alt öğeleri dikey olarak hizalamak için kullanıldığından HorizontalAlignmentLine türündedir. Birden fazla düzenin bu hizalama satırları için değer sağladığı durumlarda birleştirme politikası parametre olarak aktarılır. Oluştur düzen sistem koordinatları ve Canvas koordinatları [0, 0]'i temsil ettiğinden sol üst köşe ve x ile y ekseni aşağı doğru pozitif yöndedir. Bu nedenle MaxChartValue değeri, her zaman MinChartValue değerinden küçük olur. Bu nedenle, birleştirme politikası, maksimum grafik veri değeri referans değeri için min ve minimum grafik veri değeri referans değeri için max şeklindedir.

Özel Layout veya LayoutModifier oluştururken, alignmentLines: Map<AlignmentLine, Int> parametresi alan MeasureScope.layout yönteminde özel hizalama çizgileri belirtin.

@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 hizalama çizgilerini tüketebilir. Aşağıdaki composable, parametre olarak iki Text slotu ve veri noktası alan ve iki metni maksimum ve minimum grafik veri değerleriyle hizalayan özel bir düzen oluşturur. Bu composable'ın önizlemesi Şekil 2'de gösterilmiştir.

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