Jetpack Compose'da hizalama satırları

Düzen oluşturma modeli, AlignmentLine'i kullanarak alt yerleşimleri hizalamak ve konumlandırmak için üst düzenler tarafından kullanılabilecek özel hizalama satırları oluşturabilirsiniz. Örneğin Row, alt öğelerini hizalamak için alt çizgilerin özel hizalama çizgilerini kullanabilir.

Bir düzen, belirli bir AlignmentLine için bir değer sağladığında, düzenin ebeveynleri bu değeri, ilgili Placeable örneğindeki Placeable.get operatörünü kullanarak ölçümden sonra okuyabilir. Daha sonra, AlignmentLine, pozisyona göre çocukların konumuna karar verebilir.

Oluştur'daki bazı besteler zaten hizalama çizgileriyle gelir. Örneğin, BasicText kombinasyonu FirstBaseline ve LastBaseline hizalama satırlarını gösterir.

Aşağıdaki örnekte, firstBaselineToTop adlı özel LayoutModifier, ilk satırdan başlayarak Text öğesine dolgu eklemek için FirstBaseline metnini okur.

Şekil 1. Bir öğeye normal dolgu ekleme ile bir Metin öğesinin referansına 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ılır.

Özel hizalama çizgileri oluşturun

Özel bir Layout veya özel LayoutModifier oluştururken özel hizalama satırları sağlayabilirsiniz. Böylece diğer üst öğeler, alt öğelerini uygun şekilde hizalamak ve konumlandırmak için kullanabilir.

Aşağıdaki örnekte, MaxChartValue ve MinChartValue olmak üzere iki hizalama çizgisi gösteren özel bir BarChart bileşimi gösterilmektedir. Böylece diğer besteler, grafiğin maksimum ve minimum veri değerine hizalanabilir. Max ve Min olmak üzere iki metin öğesi, özel hizalama çizgilerinin ortasına hizalandı.

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

Özel hizalama satırları, 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 özel hizalama çizgileri HorizontalAlignmentLine türündedir. Çocukların dikey olarak hizalanması için kullanılır. Birden fazla düzenin bu hizalama satırları için bir değer sağlaması durumunda, bir birleştirme politikası parametre olarak iletilir. Yazma düzeni sistem koordinatları ve Canvas koordinatları [0, 0] değerini temsil ettiğinden sol üst köşe ile x ve y ekseni aşağı doğrudur. 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'dir.

Özel bir Layout veya LayoutModifier oluştururken MeasureScope.layout yönteminde bir 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 bestenin doğrudan ve dolaylı ebeveynleri hizalama çizgilerini tüketebilir. Aşağıdaki oluşturulabilir, iki Text slotu ve veri noktası parametresi olarak alan ve iki metni maksimum ve minimum grafik veri değerlerine uygun hale getiren özel bir düzen oluşturur. Bu bestenin ö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)
        )
    }
}