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

Compose का एक नियम यह है कि आपको अपने बच्चों को सिर्फ़ एक बार मेज़र करना चाहिए; बच्चों को दो बार मेज़र करने पर, रनटाइम अपवाद दिखता है. हालांकि, कई बार आपको अपने बच्चों के बारे में कुछ जानकारी की ज़रूरत होती है, ताकि उनकी लंबाई मापी जा सके.

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

किसी कंपोज़ेबल के लिए, IntrinsicSize.Min या IntrinsicSize.Max के बारे में पूछा जा सकता है:

  • Modifier.width(IntrinsicSize.Min) - अपने कॉन्टेंट को सही तरीके से दिखाने के लिए, आपको कम से कम कितनी चौड़ाई की ज़रूरत है?
  • Modifier.width(IntrinsicSize.Max) - कॉन्टेंट को सही तरीके से दिखाने के लिए, आपको ज़्यादा से ज़्यादा कितनी चौड़ाई चाहिए?
  • Modifier.height(IntrinsicSize.Min) - कॉन्टेंट को सही तरीके से दिखाने के लिए, कम से कम कितनी ऊंचाई की ज़रूरत होती है?
  • Modifier.height(IntrinsicSize.Max) - कॉन्टेंट को सही तरीके से दिखाने के लिए, आपको ज़्यादा से ज़्यादा कितनी ऊंचाई की ज़रूरत है?

उदाहरण के लिए, अगर कस्टम लेआउट में width की अनंत सीमाओं वाले Text के minIntrinsicHeight का अनुरोध किया जाता है, तो यह Text के height को एक लाइन में टेक्स्ट के साथ दिखाएगा.

इंट्रिंसिक फ़ंक्शन का इस्तेमाल

मान लें कि हमें एक ऐसा कंपोज़ेबल बनाना है जो स्क्रीन पर दो टेक्स्ट दिखाता है. दोनों टेक्स्ट के बीच में इस तरह का डिवाइडर होता है:

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

हम ऐसा कैसे कर सकते हैं? हमारे पास एक Row हो सकता है, जिसमें दो Text हों. ये 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
        )
        VerticalDivider(
            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 की ऊंचाई का इस्तेमाल, 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
        )
        VerticalDivider(
            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, Row की दी गई height की सीमा के हिसाब से, अपनी 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.
}