मॉडिफ़ायर लिखें

मॉडिफ़ायर की मदद से, कंपोज़ेबल को सजाने या बेहतर बनाया जा सकता है. मॉडिफ़ायर की मदद से ये काम किए जा सकते हैं:

  • कॉम्पोज़ेबल का साइज़, लेआउट, व्यवहार, और दिखावट बदलना
  • सुलभता के लेबल जैसी जानकारी जोड़ें
  • उपयोगकर्ता का इनपुट प्रोसेस करें
  • बेहतर इंटरैक्शन जोड़ें. जैसे, किसी एलिमेंट को क्लिक किया जा सकने वाला, स्क्रोल किया जा सकने वाला, खींचा और छोड़ा जा सकने वाला या ज़ूम किया जा सकने वाला बनाना

मॉडिफ़ायर, स्टैंडर्ड Kotlin ऑब्जेक्ट होते हैं. इनमें से किसी एक को कॉल करके मॉडिफ़ायर बनाएं Modifier क्लास फ़ंक्शन:

@Composable
private fun Greeting(name: String) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

रंगीन बैकग्राउंड के साथ टेक्स्ट की दो लाइन, जिसमें टेक्स्ट के चारों ओर पैडिंग (जगह) है.

इन फ़ंक्शन को एक साथ जोड़कर, उन्हें कंपोज किया जा सकता है:

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

टेक्स्ट के पीछे मौजूद रंगीन बैकग्राउंड, अब डिवाइस की पूरी चौड़ाई तक फैला हुआ है.

ऊपर दिए गए कोड में, एक साथ इस्तेमाल किए गए अलग-अलग मॉडिफ़ायर फ़ंक्शन पर ध्यान दें.

  • padding किसी एलिमेंट के आस-पास स्पेस रखता है.
  • fillMaxWidth कंपोज़ेबल फ़िल बनाता है, जिसकी वैल्यू इससे दी गई होती है इसकी पैरंट कंपनी है.

यह सबसे सही तरीका है कि सभी कंपोज़ेबल में modifier स्वीकार किया जाए पैरामीटर को कॉपी कर सकता है और उस मॉडिफ़ायर को यूज़र इंटरफ़ेस (यूआई) को उत्सर्जन करने वाले उसके पहले चाइल्ड को पास कर सकता है. ऐसा करने से, आपके कोड को फिर से इस्तेमाल करना आसान हो जाता है. साथ ही, इसके काम करने के तरीके का अनुमान लगाना और उसे समझना आसान हो जाता है. ज़्यादा जानकारी के लिए, Compose API के दिशा-निर्देश देखें. एलिमेंट, बदलाव करने वाले पैरामीटर को स्वीकार करते हैं और उसका इस्तेमाल करते हैं.

मॉडिफ़ायर का क्रम मायने रखता है

मॉडिफ़ायर फ़ंक्शन का क्रम अहम होता है. क्योंकि हर फ़ंक्शन बनाता है पिछले फ़ंक्शन से मिलने वाले Modifierमें बदलाव, क्रम आखिरी नतीजे पर असर डालता है. आइए, इसका एक उदाहरण देखें:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

किनारों के चारों ओर पैडिंग के साथ-साथ पूरा क्षेत्र, क्लिक पर प्रतिक्रिया देता है

ऊपर दिए गए कोड में, आस-पास के एरिया के साथ-साथ पूरा इलाका क्लिक किया जा सकता है पैडिंग (जगह), क्योंकि padding मॉडिफ़ायर को clickable के बाद लागू किया गया है कार्रवाई बदलने वाली कुंजी. अगर कार्रवाई बदलने वाली कुंजी का क्रम उलटा हो जाता है, तो padding की जोड़ी गई स्पेस ऐसा करती है उपयोगकर्ता के इनपुट पर प्रतिक्रिया न दें:

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

लेआउट के किनारे के पैडिंग पर क्लिक करने पर, अब कोई कार्रवाई नहीं होती

पहले से मौजूद मॉडिफ़ायर

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

padding और size

डिफ़ॉल्ट रूप से, Compose में दिए गए लेआउट अपने चाइल्ड लेआउट को रैप कर देते हैं. हालांकि, size मॉडिफ़ायर का इस्तेमाल करके, साइज़ सेट किया जा सकता है:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(/*...*/)
        Column { /*...*/ }
    }
}

ध्यान दें कि अगर आपने जो साइज़ तय किया है वह लेआउट के पैरंट से मिलने वाली पाबंदियों को पूरा नहीं करता है, तो हो सकता है कि आपके तय किए गए साइज़ का इस्तेमाल न किया जाए. अगर आपको कंपोज़ेबल आने वाली सीमाओं पर ध्यान दिए बिना, साइज़ में बदलाव करने के लिए, requiredSize का इस्तेमाल करें कार्रवाई बदलने वाली कुंजी:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.requiredSize(150.dp)
        )
        Column { /*...*/ }
    }
}

चाइल्ड इमेज, अपने पैरंट की सीमाओं से बड़ी है

इस उदाहरण में, पैरंट height को 100.dp पर सेट करने के बावजूद, Image की ऊंचाई 150.dp होगी, क्योंकि requiredSize मॉडिफ़ायर को प्राथमिकता दी जाती है.

अगर आपको लगता है कि कोई चाइल्ड लेआउट, पैरंट, fillMaxHeight मॉडिफ़ायर जोड़ें (Compose यह सुविधा भी देता है fillMaxSize और fillMaxWidth):

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.fillMaxHeight()
        )
        Column { /*...*/ }
    }
}

इमेज की ऊंचाई, उसके पैरंट की ऊंचाई के बराबर हो

किसी एलिमेंट के चारों ओर पैडिंग जोड़ने के लिए, padding मॉडिफ़ायर सेट करें.

अगर आपको टेक्स्ट बेसलाइन के ऊपर पैडिंग (जगह) जोड़ना है, ताकि लेआउट के शीर्ष से बेसलाइन की विशेष दूरी के लिए, paddingFromBaseline मॉडिफ़ायर:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(
                text = artist.name,
                modifier = Modifier.paddingFromBaseline(top = 50.dp)
            )
            Text(artist.lastSeenOnline)
        }
    }
}

टेक्स्ट के ऊपर पैडिंग

ऑफ़सेट

लेआउट को उसकी मूल जगह के मुताबिक रखने के लिए, offset मॉडिफ़ायर दें और ऑफ़सेट को x और y ऐक्सिस पर सेट करें. ऑफ़सेट, पॉज़िटिव और नॉन-पॉज़िटिव, दोनों तरह के हो सकते हैं. padding और offset के बीच का अंतर यह है कि किसी कॉम्पोज़ेबल में offset जोड़ने से, उसके मेज़रमेंट में बदलाव नहीं होता:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(artist.name)
            Text(
                text = artist.lastSeenOnline,
                modifier = Modifier.offset(x = 4.dp)
            )
        }
    }
}

टेक्स्ट को उसके पैरंट कंटेनर की दाईं ओर ले जाया गया

offset मॉडिफ़ायर, लेआउट की दिशा के हिसाब से हॉरिज़ॉन्टल तौर पर लागू होता है. बाएं से दाएं के संदर्भ में, एक पॉज़िटिव offset एलिमेंट को दाएं से बाएं संदर्भ में होने पर, यह एलिमेंट को बाईं ओर शिफ़्ट कर देता है. अगर आपको लेआउट के डायरेक्शन को ध्यान में रखे बिना ऑफ़सेट सेट करना है, तो absoluteOffset मॉडिफ़ायर देखें. इसमें, ऑफ़सेट की पॉज़िटिव वैल्यू हमेशा एलिमेंट को दाईं ओर ले जाती है.

offset मॉडिफ़ायर दो ओवरलोड देता है - offset जो ऑफ़सेट के हिसाब से पैरामीटर इस्तेमाल करता है और offset करता है, जो लैम्डा में लेता है. इनमें से किसी भी सुविधा का इस्तेमाल कब करना है और परफ़ॉर्मेंस को ऑप्टिमाइज़ करने का तरीका क्या है, इस बारे में ज़्यादा जानने के लिए, कॉम्पोज़ करने की परफ़ॉर्मेंस - रीड को ज़्यादा से ज़्यादा देर तक रोकना सेक्शन पढ़ें.

Compose में स्कोप की सुरक्षा

Compose में ऐसे मॉडिफ़ायर होते हैं जिनका इस्तेमाल सिर्फ़ कुछ कॉम्पोज़ेबल के बच्चों पर किया जा सकता है. Compose इसे कस्टम स्कोप की मदद से लागू करता है.

उदाहरण के लिए, अगर आपको किसी बच्चे को उसके माता-पिता के बराबर बड़ा बनाना है, तो Box Box के साइज़ पर असर पड़ रहा है, तो matchParentSize कार्रवाई बदलने वाली कुंजी. matchParentSize सिर्फ़ BoxScope में उपलब्ध है. इसलिए, इसका इस्तेमाल सिर्फ़ Box माता-पिता के बच्चे के लिए किया जा सकता है.

स्कोप सेफ़्टी की सुविधा, ऐसे मॉडिफ़ायर जोड़ने से रोकती है जो अन्य कॉम्पोज़ेबल और स्कोप में काम नहीं करेंगे. साथ ही, यह आपको बार-बार कोशिश करने और गड़बड़ियों को ठीक करने में लगने वाले समय को बचाती है.

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

matchParentSize, Box में है

जैसा कि ऊपर बताया गया है, अगर आपको चाइल्ड लेआउट को पैरंट के साइज़ के बराबर रखना है Box के साइज़ पर असर डाले बिना, Box, matchParentSize मॉडिफ़ायर का इस्तेमाल करें.

ध्यान दें कि matchParentSize सिर्फ़ Box के दायरे में उपलब्ध है. इसका मतलब है कि यह सिर्फ़ Box कॉम्पोज़ेबल के डायरेक्ट चाइल्ड पर लागू होता है.

नीचे दिए गए उदाहरण में, चाइल्ड Spacer का साइज़, पैरंट Box से लिया जाता है. साथ ही, पैरंट Box का साइज़, सबसे बड़े चाइल्ड ArtistCard से लिया जाता है.

@Composable
fun MatchParentSizeComposable() {
    Box {
        Spacer(
            Modifier
                .matchParentSize()
                .background(Color.LightGray)
        )
        ArtistCard()
    }
}

कंटेनर को भरने वाला धूसर बैकग्राउंड

अगर matchParentSize के बजाय fillMaxSize का इस्तेमाल किया जाता, तो Spacer को माता-पिता के लिए उपलब्ध स्टोरेज की वजह से, माता-पिता को सभी उपलब्ध जगह को बड़ा करके भरें.

धूसर रंग का बैकग्राउंड, जिससे स्क्रीन दिख रही है

Row और Column में weight

जैसा कि आपने पैडिंग और साइज़ वाले पिछले सेक्शन में देखा है, डिफ़ॉल्ट रूप से किसी कॉम्पोज़ेबल का साइज़, उस कॉन्टेंट से तय होता है जिसे रैप किया जा रहा है. कंपोज़ेबल साइज़ को इस हिसाब से सेट किया जा सकता है कि साइज़ सिर्फ़ RowScope में उपलब्ध weight मॉडिफ़ायर का इस्तेमाल करने वाले माता-पिता, और ColumnScope.

आइए, एक Row लेते हैं जिसमें दो Box कॉम्पोज़ेबल हैं. पहले बॉक्स को दूसरे बॉक्स के मुकाबले दोगुना weight दिया गया है, इसलिए उसे दोगुनी चौड़ाई दी गई है. Row 210.dp चौड़ा है, इसलिए पहला Box 140.dp चौड़ा है और दूसरा 70.dp चौड़ा है:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Image(
            /*...*/
            modifier = Modifier.weight(2f)
        )
        Column(
            modifier = Modifier.weight(1f)
        ) {
            /*...*/
        }
    }
}

इमेज की चौड़ाई, टेक्स्ट की चौड़ाई से दोगुनी हो

मॉडिफ़ायर निकालना और उनका फिर से इस्तेमाल करना

किसी कॉम्पोज़ेबल को सजाने या बेहतर बनाने के लिए, एक से ज़्यादा मॉडिफ़ायर को एक साथ जोड़ा जा सकता है. यह चेन, Modifier इंटरफ़ेस की मदद से बनाई जाती है. यह इंटरफ़ेस, Modifier.Elements की एक व्यवस्थित और अपरिवर्तनीय सूची दिखाता है.

हर Modifier.Element एक व्यक्तिगत व्यवहार दिखाता है, जैसे कि लेआउट, ड्रॉइंग और ग्राफ़िक्स से जुड़े सभी व्यवहार, फ़ोकस, और सिमेंटिक्स से जुड़े व्यवहार शामिल होते हैं. डिवाइस इनपुट इवेंट भी होते हैं. उनका क्रम मायने रखता है: कार्रवाई बदलने वाली चीज़ें पहले जोड़ी गई वैल्यू को पहले लागू किया जाएगा.

कभी-कभी, कार्रवाई बदलने वाली एक ही चेन के इंस्टेंस का दोबारा इस्तेमाल करना फ़ायदेमंद हो सकता है अलग-अलग कंपोज़ेबल को वैरिएबल में एक्सट्रैक्ट करके और उन्हें एक साथ घुमाया जा सकता है बड़े दायरे होते हैं. इससे कोड को पढ़ने में आसानी होती है या आपके ऐप्लिकेशन की परफ़ॉर्मेंस को बेहतर बनाने में मदद मिलती है. ऐसा इन वजहों से होता है:

  • बदलाव होने पर, मॉडिफ़ायर का फिर से आवंटन नहीं होगा उन कंपोज़ेबल के लिए जिनमें इसका इस्तेमाल किया जाता है
  • संशोधक शृंखलाएं बहुत लंबी और जटिल हो सकती हैं, इसलिए चेन में एक ही इंस्टेंस होने पर, वर्कलोड Compose के रनटाइम की ज़रूरत कम हो सकती है उनकी तुलना करते समय
  • कोड को अलग करने से, कोडबेस में कोड को साफ़ और एक जैसा रखने के साथ-साथ, उसे मैनेज करने में भी मदद मिलती है

मॉडिफ़ायर का फिर से इस्तेमाल करने के सबसे सही तरीके

अपनी Modifier चेन बनाएं और एक से ज़्यादा चेन में उनका फिर से इस्तेमाल करने के लिए, उन्हें एक्सट्रैक्ट करें कंपोज़ेबल कॉम्पोनेंट. सिर्फ़ एक मॉडिफ़ायर सेव करना पूरी तरह से ठीक है, क्योंकि ये डेटा जैसे ऑब्जेक्ट होते हैं:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

बार-बार बदलने वाली स्थिति को देखते समय, मॉडिफ़ायर निकालना और दोबारा इस्तेमाल करना

ऐनिमेशन के स्टेटस या scrollState जैसे कॉम्पोज़ेबल में, अक्सर बदलने वाले स्टेटस को देखते समय, कॉम्पोज़िशन को कई बार फिर से बनाया जा सकता है. इस मामले में, आपके मॉडिफ़ायर को हर बार बदलाव के बाद बांटा जाएगा हर फ़्रेम के लिए:

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // Creation and allocation of this modifier will happen on every frame of the animation!
        modifier = Modifier
            .padding(12.dp)
            .background(Color.Gray),
        animatedState = animatedState
    )
}

इसके बजाय, मॉडिफ़ायर का एक ही इंस्टेंस बनाया जा सकता है, उसे निकाला जा सकता है, और फिर से इस्तेमाल किया जा सकता है. साथ ही, उसे इस तरह से कॉम्पोज़ेबल में पास किया जा सकता है:

// Now, the allocation of the modifier happens here:
val reusableModifier = Modifier
    .padding(12.dp)
    .background(Color.Gray)

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // No allocation, as we're just reusing the same instance
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

अनस्कोप वाले मॉडिफ़ायर निकालना और उनका दोबारा इस्तेमाल करना

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

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

@Composable
fun AuthorField() {
    HeaderText(
        // ...
        modifier = reusableModifier
    )
    SubtitleText(
        // ...
        modifier = reusableModifier
    )
}

यह सुविधा, लेज़ी लेआउट के साथ इस्तेमाल करने पर खास तौर पर फ़ायदेमंद हो सकती है. ज़्यादातर मामलों में, आप चाहते हैं कि आपके सभी, संभावित रूप से महत्वपूर्ण आइटम बिलकुल एक जैसे मॉडिफ़ायर:

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // ...
                modifier = reusableItemModifier,
            )
        }
    }
}

स्कोप वाले मॉडिफ़ायर निकालना और दोबारा इस्तेमाल करना

कुछ कंपोज़ेबल के दायरे में आने वाले मॉडिफ़ायर के साथ काम करते समय, उन्हें सबसे ज़्यादा संभावित लेवल तक एक्सट्रैक्ट करें और जहां भी सही हो वहां फिर से इस्तेमाल करें:

Column(/*...*/) {
    val reusableItemModifier = Modifier
        .padding(bottom = 12.dp)
        // Align Modifier.Element requires a ColumnScope
        .align(Alignment.CenterHorizontally)
        .weight(1f)
    Text1(
        modifier = reusableItemModifier,
        // ...
    )
    Text2(
        modifier = reusableItemModifier
        // ...
    )
    // ...
}

आपको सिर्फ़ एक्सट्रैक्ट किए गए, स्कोप वाले मॉडिफ़ायर को एक ही स्कोप वाले, डायरेक्ट चाइल्ड. इसके हिसाब से, स्कोप की सुरक्षा यह ज़रूरी क्यों है, इस बारे में ज़्यादा जानने के लिए लिखें:

Column(modifier = Modifier.fillMaxWidth()) {
    // Weight modifier is scoped to the Column composable
    val reusableItemModifier = Modifier.weight(1f)

    // Weight will be properly assigned here since this Text is a direct child of Column
    Text1(
        modifier = reusableItemModifier
        // ...
    )

    Box {
        Text2(
            // Weight won't do anything here since the Text composable is not a direct child of Column
            modifier = reusableItemModifier
            // ...
        )
    }
}

निकाले गए मॉडिफ़ायर को चेन में जोड़ना

आप .then() फ़ंक्शन:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

// Append to your reusableModifier
reusableModifier.clickable { /*...*/ }

// Append your reusableModifier
otherModifier.then(reusableModifier)

बस ध्यान रखें कि बदलाव करने वाले निर्देशों का क्रम मायने रखता है!

ज़्यादा जानें

हमने मॉडिफ़ायर की पूरी सूची दी है. इसमें पैरामीटर और दायरों की जानकारी.

मॉडिफ़ायर इस्तेमाल करने के तरीके के बारे में ज़्यादा जानने के लिए, Compose कोडलैब में बुनियादी लेआउट देखें या Android के रिपॉज़िटरी में अब उपलब्ध है लेख पढ़ें.

कस्टम मॉडिफ़ायर और उन्हें बनाने के तरीके के बारे में ज़्यादा जानने के लिए, कस्टम लेआउट - लेआउट मॉडिफ़ायर का इस्तेमाल करना दस्तावेज़ देखें.