Garis perataan di Jetpack Compose

Model tata letak Compose memungkinkan Anda menggunakan AlignmentLine untuk membuat garis perataan kustom yang dapat digunakan oleh tata letak induk untuk meratakan dan memosisikan turunannya. Misalnya, Row dapat menggunakan garis perataan kustom turunannya untuk meratakannya.

Saat tata letak memberikan nilai untuk AlignmentLine tertentu, induk tata letak dapat membaca nilai ini setelah melakukan pengukuran, menggunakan operator Placeable.get pada instance Placeable yang terkait. Berdasarkan posisi AlignmentLine, induk kemudian dapat memutuskan posisi turunan.

Beberapa composable di Compose sudah dilengkapi dengan garis perataan. Misalnya, composable BasicText menampilkan garis perataan FirstBaseline dan LastBaseline.

Dalam contoh berikut, LayoutModifier kustom yang disebut firstBaselineToTop membaca FirstBaseline untuk menambahkan padding ke Text mulai dari dasar pengukuran pertamanya.

Menampilkan perbedaan antara menambahkan padding normal ke elemen, dan menerapkan padding ke dasar pengukuran elemen Teks.
Gambar 1. Menampilkan perbedaan antara menambahkan padding normal ke elemen, dan menerapkan padding ke dasar pengukuran elemen Teks.

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

Untuk membaca FirstBaseline dalam contoh ini, placeable [FirstBaseline] digunakan dalam fase pengukuran.

Membuat garis perataan khusus

Saat membuat composable Layout kustom atau LayoutModifier kustom, Anda dapat memberikan garis perataan kustom sehingga composable induk lain dapat menggunakannya untuk meratakan dan memosisikan turunannya sebagaimana mestinya.

Contoh berikut menunjukkan composable BarChart kustom yang mengekspos dua garis perataan, MaxChartValue, dan MinChartValue, sehingga composable lain dapat diratakan dengan nilai data minimum dan minimum pada diagram. Dua elemen teks, Maks dan Min, telah diratakan ke tengah garis perataan khusus.

Composable BarChart dengan Teks yang diratakan dengan nilai data maksimum dan minimum.
Gambar 2. Composable BarChart dengan Teks yang diratakan dengan nilai data maksimum dan minimum.

Garis perataan khusus ditetapkan sebagai variabel level atas dalam project Anda.

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

Garis perataan khusus untuk membuat contoh adalah jenis HorizontalAlignmentLine, karena digunakan untuk meratakan turunan secara vertikal. Kebijakan penggabungan diteruskan sebagai parameter jika beberapa tata letak memberikan nilai untuk garis perataan ini. Karena koordinat sistem tata letak Compose dan koordinat Canvas mewakili [0, 0], pojok kiri atas dan sumbu x dan y positif ke bawah, sehingga nilai MaxChartValue akan selalu lebih kecil dari MinChartValue. Oleh karena itu, kebijakan penggabungannya adalah min untuk dasar pengukuran nilai data diagram maksimum, dan max untuk dasar pengukuran nilai data diagram minimum.

Saat membuat Layout atau LayoutModifier kustom, tentukan garis perataan khusus dalam metode MeasureScope.layout, yang menggunakan parameter alignmentLines: Map<AlignmentLine, Int>.

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

Induk langsung dan tidak langsung dari composable ini dapat menggunakan garis perataan. Composable berikut akan membuat tata letak kustom yang menggunakan parameter, dua slot Text dan titik data, serta meratakan dua teks tersebut ke nilai data diagram maksimum dan minimum. Pratinjau composable ini adalah yang ditampilkan pada Gambar 2.

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