कस्टम लेआउट

Compose में, यूज़र इंटरफ़ेस (यूआई) एलिमेंट को ऐसे फ़ंक्शन से दिखाया जाता है जिन्हें इस्तेमाल करने पर, यूआई का एक हिस्सा दिखता है. इसके बाद, उस हिस्से को यूआई ट्री में जोड़ दिया जाता है, जो स्क्रीन पर रेंडर होता है. हर यूज़र इंटरफ़ेस (यूआई) एलिमेंट का एक पैरंट होता है और उसमें कई चाइल्ड एलिमेंट हो सकते हैं. हर एलिमेंट अपने पैरंट में भी मौजूद होता है. इसकी पोज़िशन (x, y) के तौर पर और साइज़ width और height के तौर पर तय की जाती है.

माता-पिता अपने चाइल्ड एलिमेंट के लिए पाबंदियां तय करते हैं. किसी एलिमेंट से कहा जाता है कि वह उन सीमाओं के मुताबिक अपना साइज़ तय करे. पाबंदियां, किसी एलिमेंट के width और height की कम से कम और ज़्यादा से ज़्यादा वैल्यू तय करती हैं. अगर किसी एलिमेंट में चाइल्ड एलिमेंट हैं, तो अपने साइज़ का पता लगाने के लिए, वह हर चाइल्ड एलिमेंट को मेज़र कर सकता है. जब कोई एलिमेंट अपने साइज़ का पता लगा लेता है और उसकी जानकारी देता है, तो उसके पास यह तय करने का विकल्प होता है कि उसके चाइल्ड एलिमेंट को अपने हिसाब से कैसे रखा जाए. इस बारे में ज़्यादा जानकारी कस्टम लेआउट बनाना में दी गई है.

यूज़र इंटरफ़ेस (यूआई) ट्री में हर नोड को तीन चरणों में व्यवस्थित किया जाता है. हर नोड के लिए ये ज़रूरी हैं:

  1. किसी भी चाइल्ड एट्रिब्यूट को मेज़र करना
  2. अपने साइज़ का फ़ैसला खुद लेता है
  3. अपने बच्चों को

नोड लेआउट के तीन चरण: चाइल्ड नोड का साइज़ तय करना, साइज़ तय करना, और चाइल्ड नोड को प्लेस करना

स्कोप का इस्तेमाल करके यह तय किया जा सकता है कि कब अपने बच्चों की गतिविधियों को मेज़र किया जाए और उन्हें जगह दी जाए. किसी लेआउट को सिर्फ़ मेज़रमेंट और लेआउट पास के दौरान मेज़र किया जा सकता है. साथ ही, किसी चाइल्ड को सिर्फ़ लेआउट पास के दौरान (और सिर्फ़ मेज़र करने के बाद) रखा जा सकता है. MeasureScope और PlacementScope जैसे Compose स्कोप की वजह से, यह कंपाइल के समय लागू होता है.

लेआउट मॉडिफ़ायर का इस्तेमाल करना

layout मॉडिफ़ायर का इस्तेमाल करके, किसी एलिमेंट को मेज़र करने और उसका लेआउट तय करने के तरीके में बदलाव किया जा सकता है. Layout एक lambda है. इसके पैरामीटर में वह एलिमेंट शामिल होता है जिसे मेज़र किया जा सकता है. इसे measurable के तौर पर पास किया जाता है. साथ ही, उस composable की इनकमिंग कंस्ट्रेंट को constraints के तौर पर पास किया जाता है. कस्टम लेआउट मॉडिफ़ायर कुछ ऐसा दिख सकता है:

fun Modifier.customLayoutModifier() =
    layout { measurable, constraints ->
        // ...
    }

आइए, स्क्रीन पर Text दिखाएं और टेक्स्ट की पहली लाइन के सबसे ऊपर से लेकर आधार तक की दूरी को कंट्रोल करें. paddingFromBaseline मॉडिफ़ायर ठीक यही काम करता है. हम इसे उदाहरण के तौर पर यहां लागू कर रहे हैं. इसके लिए, layout मॉडिफ़ायर का इस्तेमाल करके, स्क्रीन पर कॉम्पोज़ेबल को मैन्युअल तरीके से डालें. यहां Text टॉप पैडिंग को 24.dp पर सेट करने पर, मनमुताबिक व्यवहार दिखता है:

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

स्पेस बनाने के लिए यह कोड इस्तेमाल करें:

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

इस कोड में क्या हो रहा है, यहां देखें:

  1. measurable लैम्ब्डा पैरामीटर में, measurable.measure(constraints) को कॉल करके, मेज़र किए जा सकने वाले पैरामीटर से दिखाए गए Text को मेज़र किया जाता है.
  2. layout(width, height) method को कॉल करके, कंपोज़ेबल का साइज़ तय किया जाता है. इससे, रैप किए गए एलिमेंट को रखने के लिए इस्तेमाल किया जाने वाला एक लैंब्डा भी मिलता है. इस मामले में, यह आखिरी बेसलाइन और ऊपर जोड़ी गई पैडिंग के बीच की ऊंचाई होती है.
  3. placeable.place(x, y) को कॉल करके, रैप किए गए एलिमेंट को स्क्रीन पर पोज़िशन किया जाता है. अगर रैप किए गए एलिमेंट नहीं डाले जाते हैं, तो वे दिखेंगे नहीं. yपोज़िशन, टॉप पैडिंग से जुड़ी होती है - टेक्स्ट के पहले बेसलाइन की पोज़िशन.

यह पुष्टि करने के लिए कि यह उम्मीद के मुताबिक काम करता है, Text पर इस मॉडिफ़ायर का इस्तेमाल करें:

@Preview
@Composable
fun TextWithPaddingToBaselinePreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
    }
}

@Preview
@Composable
fun TextWithNormalPaddingPreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.padding(top = 32.dp))
    }
}

टेक्स्ट एलिमेंट की कई झलकें; एक में एलिमेंट के बीच सामान्य पैडिंग दिखती है, जबकि दूसरे में एक बेसलाइन से अगले बेसलाइन तक पैडिंग दिखती है

कस्टम लेआउट बनाना

layout मॉडिफ़ायर सिर्फ़ कॉलिंग कॉम्पोज़ेबल को बदलता है. एक से ज़्यादा कॉम्पोज़ेबल को मेज़र और लेआउट करने के लिए, Layout कॉम्पोज़ेबल का इस्तेमाल करें. इस कॉम्पोज़ेबल की मदद से, बच्चों को मैन्युअल तरीके से मेज़र और लेआउट किया जा सकता है. Column और Row जैसे सभी हाई लेवल लेआउट, Layout कॉम्पोज़ेबल की मदद से बनाए जाते हैं.

आइए, Column का एक बुनियादी वर्शन बनाते हैं. ज़्यादातर कस्टम लेआउट इस पैटर्न का पालन करते हैं:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // measure and position children given constraints logic here
        // ...
    }
}

layout मॉडिफ़ायर की तरह ही, measurables उन बच्चों की सूची है जिनका आकलन करना है और constraints माता-पिता की ओर से लगाई गई पाबंदियां हैं. पहले की तरह ही लॉजिक का इस्तेमाल करके, MyBasicColumn को इस तरह लागू किया जा सकता है:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

चाइल्ड कॉम्पोनेंट, Layout की पाबंदियों (minHeight की पाबंदियों के बिना) के हिसाब से होते हैं. साथ ही, उन्हें पिछले कॉम्पोनेंट के yPosition के आधार पर रखा जाता है.

कस्टम कॉम्पोज़ेबल का इस्तेमाल इस तरह किया जाएगा:

@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
    MyBasicColumn(modifier.padding(8.dp)) {
        Text("MyBasicColumn")
        Text("places items")
        Text("vertically.")
        Text("We've done it by hand!")
    }
}

कॉलम में एक के ऊपर एक करके रखे गए कई टेक्स्ट एलिमेंट.

लेआउट की दिशा

LocalLayoutDirection कंपोज़िशन लोकल बदलकर, किसी कॉम्पोज़ेबल के लेआउट की दिशा बदलें.

अगर स्क्रीन पर कॉम्पोनेंबल को मैन्युअल तरीके से डाला जा रहा है, तो LayoutDirection, layout मॉडिफ़ायर या Layout कॉम्पोनेंबल के LayoutScope का हिस्सा है.

layoutDirection का इस्तेमाल करते समय, place का इस्तेमाल करके कॉम्पोनेंट डालें. placeRelative के तरीके के उलट, place लेआउट की दिशा (बाईं से दाईं बनाम दाईं से बाईं) के आधार पर नहीं बदलता.

कस्टम लेआउट इस्तेमाल करने का तरीका

Compose में बुनियादी लेआउट में, लेआउट और मॉडिफ़ायर के बारे में ज़्यादा जानें. साथ ही, कस्टम लेआउट बनाने वाले Compose के सैंपल में, कस्टम लेआउट को काम करते हुए देखें.

ज़्यादा जानें

Compose में कस्टम लेआउट के बारे में ज़्यादा जानने के लिए, यहां दिए गए अन्य संसाधन देखें.

वीडियो