Jetpack Compose में अलाइनमेंट लाइनें

Compose लेआउट मॉडल की मदद से, AlignmentLine का इस्तेमाल करके, अलाइनमेंट की कस्टम लाइनें बनाई जा सकती हैं. इनका इस्तेमाल पैरंट लेआउट, अपने चाइल्ड को अलाइन और पोज़िशन करने के लिए कर सकते हैं. उदाहरण के लिए, Row अपने बच्चों को अलाइन करने के लिए, कस्टम अलाइनमेंट लाइनों का इस्तेमाल कर सकता है.

जब कोई लेआउट किसी AlignmentLine के लिए वैल्यू देता है, तो लेआउट के पैरंट, मेज़रमेंट के बाद इस वैल्यू को पढ़ सकते हैं. इसके लिए, उन्हें Placeable इंस्टेंस पर Placeable.get ऑपरेटर का इस्तेमाल करना होगा. AlignmentLine की पोज़िशन के आधार पर, माता-पिता बच्चों की पोज़िशन तय कर सकते हैं.

Compose में कुछ कंपोज़ेबल में, अलाइनमेंट लाइनें पहले से मौजूद होती हैं. उदाहरण के लिए, BasicText कंपोज़ेबल, FirstBaseline और LastBaseline अलाइनमेंट लाइन दिखाता है.

यहां दिए गए उदाहरण में, firstBaselineToTop नाम का कस्टम LayoutModifier, FirstBaseline को पढ़कर Text में पैडिंग जोड़ता है. यह पैडिंग, Text की पहली बेसलाइन से शुरू होती है.

इस इमेज में, किसी एलिमेंट में सामान्य पैडिंग जोड़ने और टेक्स्ट एलिमेंट की बेसलाइन में पैडिंग जोड़ने के बीच का अंतर दिखाया गया है.
पहली इमेज. इस इमेज में, किसी एलिमेंट में सामान्य पैडिंग जोड़ने और टेक्स्ट एलिमेंट की बेसलाइन में पैडिंग जोड़ने के बीच का अंतर दिखाया गया है.

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

उदाहरण में दिए गए FirstBaseline को पढ़ने के लिए, मेज़रमेंट फ़ेज़ में placeable [FirstBaseline] का इस्तेमाल किया जाता है.

कस्टम अलाइनमेंट लाइनें बनाना

कस्टम Layout कंपोज़ेबल या कस्टम LayoutModifier बनाते समय, कस्टम अलाइनमेंट लाइनें दी जा सकती हैं. इससे अन्य पैरंट कंपोज़ेबल, इनका इस्तेमाल करके अपने चाइल्ड को अलाइन और पोज़िशन कर सकते हैं.

यहां दिए गए उदाहरण में, कस्टम BarChart कंपोज़ेबल दिखाया गया है. यह दो अलाइनमेंट लाइनें, MaxChartValue और MinChartValue दिखाता है, ताकि अन्य कंपोज़ेबल, चार्ट की सबसे ज़्यादा और सबसे कम डेटा वैल्यू के साथ अलाइन हो सकें. दो टेक्स्ट एलिमेंट, ज़्यादा से ज़्यादा और कम से कम, को कस्टम अलाइनमेंट लाइनों के बीच में अलाइन किया गया है.

BarChart कंपोज़ेबल, जिसमें टेक्स्ट को ज़्यादा से ज़्यादा और कम से कम डेटा वैल्यू के साथ अलाइन किया गया है.
दूसरी इमेज. BarChart को टेक्स्ट के साथ कंपोज़ किया जा सकता है. टेक्स्ट को डेटा की सबसे बड़ी और सबसे छोटी वैल्यू के साथ अलाइन किया जाता है.

कस्टम अलाइनमेंट लाइनों को आपके प्रोजेक्ट में टॉप लेवल वैरिएबल के तौर पर तय किया जाता है.

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

उदाहरण बनाने के लिए, कस्टम अलाइनमेंट लाइनें HorizontalAlignmentLine टाइप की हैं. इनका इस्तेमाल बच्चों को वर्टिकल तरीके से अलाइन करने के लिए किया जाता है. अगर कई लेआउट, अलाइनमेंट लाइनों के लिए वैल्यू देते हैं, तो मर्ज करने की नीति को पैरामीटर के तौर पर पास किया जाता है. Compose लेआउट सिस्टम के कोऑर्डिनेट और Canvas कोऑर्डिनेट, [0, 0] को दिखाते हैं. इसलिए, ऊपर बायां कोना और x और y ऐक्सिस नीचे की ओर पॉज़िटिव होते हैं. इसलिए, MaxChartValue की वैल्यू हमेशा MinChartValue से कम होगी. इसलिए, चार्ट में मौजूद डेटा की सबसे बड़ी वैल्यू के लिए, मर्ज करने की नीति min है. वहीं, चार्ट में मौजूद डेटा की सबसे छोटी वैल्यू के लिए, मर्ज करने की नीति max है.

कस्टम Layout या LayoutModifier बनाते समय, MeasureScope.layout तरीके में कस्टम अलाइनमेंट लाइनें तय करें. यह 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()
                        )
                    ) {}
                }
            }
        }
    }
}

इस कंपोज़ेबल के डायरेक्ट और इनडायरेक्ट पैरंट, अलाइनमेंट लाइन का इस्तेमाल कर सकते हैं. नीचे दिए गए कंपोज़ेबल से एक कस्टम लेआउट बनाया जाता है. यह लेआउट, पैरामीटर के तौर पर दो Text स्लॉट और डेटा पॉइंट लेता है. साथ ही, दोनों टेक्स्ट को चार्ट के ज़्यादा से ज़्यादा और कम से कम डेटा वैल्यू के साथ अलाइन करता है. इस कंपोज़ेबल की झलक, दूसरी इमेज में दिखाई गई है.

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