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

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

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

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

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

उदाहरण के लिए, अगर किसी Text के अनलिमिटेड height के लिए 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.
}