मॉडिफ़ायर की मदद से, किसी कॉम्पोज़ेबल को सजाया या बेहतर बनाया जा सकता है. मॉडिफ़ायर की मदद से ये काम किए जा सकते हैं:
- कॉम्पोज़ेबल का साइज़, लेआउट, व्यवहार, और दिखावट बदलना
- सुलभता के लेबल जैसी जानकारी जोड़ें
- उपयोगकर्ता के इनपुट को प्रोसेस करना
- बेहतर इंटरैक्शन जोड़ें. जैसे, किसी एलिमेंट को क्लिक किया जा सकने वाला, स्क्रोल किया जा सकने वाला, खींचा और छोड़ा जा सकने वाला या ज़ूम किया जा सकने वाला बनाना
मॉडिफ़ायर, स्टैंडर्ड 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 } }
ऊपर दिए गए कोड में, पूरे एरिया पर क्लिक किया जा सकता है. इसमें आस-पास की पैडिंग भी शामिल है, क्योंकि clickable
बदलाव करने वाले टूल के बाद padding
बदलाव करने वाले टूल का इस्तेमाल किया गया है. अगर मॉडिफ़ायर का क्रम उलट दिया जाता है, तो 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
में है
जैसा कि ऊपर बताया गया है, अगर आपको चाइल्ड लेआउट का साइज़, पैरंट लेआउट के साइज़ के बराबर रखना है, तो matchParentSize
मॉडिफ़ायर का इस्तेमाल करें. इससे Box
के साइज़ पर कोई असर नहीं पड़ेगा.Box
ध्यान दें कि 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
और ColumnScope
में उपलब्ध weight
मॉडिफ़ायर का इस्तेमाल करके, कंपोज़ेबल साइज़ को उसके पैरंट के हिसाब से सुविधाजनक बनाने के लिए सेट किया जा सकता है.
आइए, एक 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 के रनटाइम पर कम काम पड़ता है. ऐसा तब होता है, जब 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 रिपॉज़िटरी में देखें.
कस्टम मॉडिफ़ायर और उन्हें बनाने के तरीके के बारे में ज़्यादा जानकारी के लिए, कस्टम लेआउट - लेआउट मॉडिफ़ायर का इस्तेमाल करना से जुड़ा दस्तावेज़ देखें.
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर लिंक टेक्स्ट दिखता है
- लेआउट बनाने की बुनियादी बातें
- एडिटर की कार्रवाइयां {:#एडिटोरियल-कार्रवाइयां}
- कस्टम लेआउट {:#custom-layouts }