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 कंपोज़ेबल होते हैं, जो उपलब्ध जगह को भरते हैं. साथ ही, बीच में 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) अपने चाइल्ड एलिमेंट की ऊंचाई को उसकी कम से कम इंट्रिंसिक ऊंचाई के बराबर सेट करता है. यह मॉडिफ़ायर रिकर्सिव है. इसलिए, यह minIntrinsicHeight और उसके चाइल्ड नोड के minIntrinsicHeight को क्वेरी करता है.Row

इस मॉडिफ़ायर को अपने कोड में जोड़ने पर, कोड आपकी उम्मीद के मुताबिक काम करता है:

@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 की ऊंचाई इस तरह तय की जाती है:

  • 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.
}