Compose के लेआउट में इंट्रिंसिक मेज़रमेंट

Compose का एक नियम यह है कि आपको अपने बच्चों को सिर्फ़ एक बार मेज़र करना चाहिए. अगर बच्चों को दो बार मेज़र किया जाता है, तो रनटाइम एक्सप्शन दिखता है. हालांकि, कभी-कभी ऐसा होता है कि बच्चों की परफ़ॉर्मेंस का आकलन करने से पहले, आपको उनके बारे में कुछ जानकारी चाहिए.

Intrinsics की मदद से, बच्चों की परफ़ॉर्मेंस का आकलन करने से पहले ही उनके बारे में जानकारी हासिल की जा सकती है.

किसी कॉम्पोज़ेबल के लिए, intrinsicWidth या intrinsicHeight का अनुरोध किया जा सकता है:

  • (min|max)IntrinsicWidth: इस चौड़ाई को देखते हुए, अपने कॉन्टेंट को पेंट करने के लिए कम से कम/ज़्यादा से ज़्यादा चौड़ाई कितनी होनी चाहिए?
  • (min|max)IntrinsicHeight: इस ऊंचाई को देखते हुए, अपने कॉन्टेंट को पेंट करने के लिए कम से कम/ज़्यादा से ज़्यादा ऊंचाई कितनी होनी चाहिए?

उदाहरण के लिए, अगर अनगिनत height वाले Text के minIntrinsicHeight से पूछा जाता है, तो यह Text का height दिखाएगा. ऐसा तब होगा, जैसे टेक्स्ट किसी एक लाइन में बनाया गया हो.

Intrinsics का इस्तेमाल

मान लें कि हमें एक ऐसा कॉम्पोज़ेबल बनाना है जो स्क्रीन पर दो टेक्स्ट दिखाता हो और उन्हें इस तरह से अलग करता हो:

एक-दूसरे के बगल में मौजूद दो टेक्स्ट एलिमेंट, जिनके बीच वर्टिकल डिवाइडर है

हम यह कैसे कर सकते हैं? हमारे पास Row में दो Text हो सकते हैं, जो ज़्यादा से ज़्यादा बड़े हो सकते हैं और बीच में एक Divider हो सकता है. हम चाहते हैं कि Divider सबसे ऊंचे Text के बराबर और सबसे पतला (width = 1.dp) हो.

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )
        HorizontalDivider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

इसकी झलक देखने पर, हमें पता चलता है कि Divider पूरी स्क्रीन पर बड़ा हो जाता है और हम यह नहीं चाहते:

एक साथ दिखने वाले दो टेक्स्ट एलिमेंट, जिनके बीच एक डिवाइडर है. हालांकि, डिवाइडर टेक्स्ट के नीचे तक फैला हुआ है

ऐसा इसलिए होता है, क्योंकि Row हर बच्चे की ऊंचाई को अलग-अलग मेज़र करता है. साथ ही, Text की सीमा तय करने के लिए, Text की ऊंचाई का इस्तेमाल नहीं किया जा सकता.Divider हम चाहते हैं कि Divider, उपलब्ध जगह को तय की गई ऊंचाई के हिसाब से भर दे. इसके लिए, हम height(IntrinsicSize.Min) मॉडिफ़ायर का इस्तेमाल कर सकते हैं .

height(IntrinsicSize.Min) अपने बच्चों को उनकी कम से कम प्राकृतिक ऊंचाई के बराबर ऊंचा करने के लिए मजबूर करता है. यह फ़ंक्शन बार-बार चलने वाला है, इसलिए यह Row और उसके minIntrinsicHeight बच्चों की क्वेरी करेगा.

इसे हमारे कोड में लागू करने पर, यह उम्मीद के मुताबिक काम करेगा:

@Composable
fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) {
    Row(modifier = modifier.height(IntrinsicSize.Min)) {
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(start = 4.dp)
                .wrapContentWidth(Alignment.Start),
            text = text1
        )
        HorizontalDivider(
            color = Color.Black,
            modifier = Modifier.fillMaxHeight().width(1.dp)
        )
        Text(
            modifier = Modifier
                .weight(1f)
                .padding(end = 4.dp)
                .wrapContentWidth(Alignment.End),

            text = text2
        )
    }
}

// @Preview
@Composable
fun TwoTextsPreview() {
    MaterialTheme {
        Surface {
            TwoTexts(text1 = "Hi", text2 = "there")
        }
    }
}

झलक के साथ:

एक-दूसरे के बगल में मौजूद दो टेक्स्ट एलिमेंट, जिनके बीच वर्टिकल डिवाइडर है

Row कॉम्पोज़ेबल का minIntrinsicHeight, उसके बच्चों का ज़्यादा से ज़्यादा minIntrinsicHeight होगा. Divider एलिमेंट का minIntrinsicHeight 0 है, क्योंकि अगर कोई कंस्ट्रेंट नहीं किया गया है, तो यह स्पेस नहीं लेता है. Text minIntrinsicHeight, खास width वाले टेक्स्ट में से एक होगा. इसलिए, Row एलिमेंट का height कंस्ट्रेंट, Text का ज़्यादा से ज़्यादा minIntrinsicHeight होगा. इसके बाद, Divider अपने height को Row की तय की गई height सीमा तक बढ़ा देगा.

पसंद के मुताबिक लेआउट में इंट्रिनिक

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

अपने कस्टम Layout के अंदरूनी माप तय करने के लिए, MeasurePolicy इंटरफ़ेस को बनाते समय minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth, और maxIntrinsicHeight को बदलें.

@Composable
fun MyCustomComposable(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        content = content,
        modifier = modifier,
        measurePolicy = object : MeasurePolicy {
            override fun MeasureScope.measure(
                measurables: List<Measurable>,
                constraints: Constraints
            ): MeasureResult {
                // Measure and layout here
                // ...
            }

            override fun IntrinsicMeasureScope.minIntrinsicWidth(
                measurables: List<IntrinsicMeasurable>,
                height: Int
            ): Int {
                // Logic here
                // ...
            }

            // Other intrinsics related methods have a default value,
            // you can override only the methods that you need.
        }
    )
}

अपना कस्टम layout मॉडिफ़ायर बनाते समय, LayoutModifier इंटरफ़ेस में उससे जुड़े तरीकों को बदलें.

fun Modifier.myCustomModifier(/* ... */) = this then object : LayoutModifier {

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {
        // Measure and layout here
        // ...
    }

    override fun IntrinsicMeasureScope.minIntrinsicWidth(
        measurable: IntrinsicMeasurable,
        height: Int
    ): Int {
        // Logic here
        // ...
    }

    // Other intrinsics related methods have a default value,
    // you can override only the methods that you need.
}