Jetpack Compose'da hizalama çizgileri

Oluştur düzen modeli, AlignmentLine aracılığıyla üst düzenlerin alt öğelerini hizalamak ve konumlandırmak için kullanılabilecek özel hizalama çizgileri oluşturmanıza olanak tanır. Örneğin, Row alt öğelerini hizalamak için özel hizalama satırlarını kullanabilir.

Bir 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. AlignmentLine cihazının konumuna göre, çocukların yerine ebeveynler karar verebilir.

Compose'daki bazı composable'lar zaten hizalama satırlarıyla birlikte gelir. Örneğin, BasicText composable, FirstBaseline ve LastBaseline hizalama satırlarını gösterir.

Aşağıdaki örnekte firstBaselineToTop adlı özel bir LayoutModifier, Text öğesine ilk referans çizgisinden başlayarak dolgu eklemek için FirstBaseline değerini okur.

Şekil 1. Bir öğeye normal dolgu ekleme ile bir 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 işlevinin okunması için ölçüm aşamasında placeable [FirstBaseline] kullanılmıştır.

Özel hizalama çizgileri oluşturma

Özel Layout composable veya özel LayoutModifier oluştururken diğer üst composable'ların bunları kullanarak alt öğelerini uygun şekilde hizalayıp konumlandırmak için özel hizalama satırları sağlayabilirsiniz.

Aşağıdaki örnekte, diğer composable'ların grafiğin maksimum ve minimum veri değerine uyum sağlaması için iki hizalama çizgisi (MaxChartValue ve MinChartValue) sunan özel bir BarChart composable gösterilmektedir. İki metin öğesi (Max ve Min) özel hizalama çizgilerinin ortasına hizalandı.

Şekil 2. BarChart maksimum ve minimum veri değerine ayarlı Metin ile derlenebilir.

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

Özel Layout veya LayoutModifier oluştururken MeasureScope.layout yönteminde alignmentLines: Map<AlignmentLine, Int> parametresi alan özel hizalama satırları 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 satırlarını kullanabilir. Aşağıdaki composable, parametre olarak iki Text yuvası ve veri noktası alan özel bir düzen oluşturur ve bu iki metni maksimum ve minimum grafik verisi değerlerine hizalar. 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)
        )
    }
}