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

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