अलग-अलग डिसप्ले साइज़ के साथ काम करना

अलग-अलग डिसप्ले साइज़ के लिए सहायता उपलब्ध होने से, ज़्यादा से ज़्यादा डिवाइसों और उपयोगकर्ताओं को आपके ऐप्लिकेशन का ऐक्सेस मिल पाता है.

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

रिस्पॉन्सिव/अडैप्टिव लेआउट, डिसप्ले के लिए उपलब्ध जगह के हिसाब से बदलते हैं. बदलावों में, लेआउट में छोटे-मोटे बदलाव शामिल हैं. जैसे, जगह भरने के लिए लेआउट को अडजस्ट करना (रिस्पॉन्सिव डिज़ाइन). इसके अलावा, एक लेआउट को पूरी तरह से दूसरे लेआउट से बदलना भी शामिल है, ताकि आपका ऐप्लिकेशन अलग-अलग डिसप्ले साइज़ के हिसाब से सबसे सही तरीके से काम कर सके (अडैप्टिव डिज़ाइन).

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

कॉन्टेंट लेवल के कंपोज़ेबल के लिए, लेआउट में बड़े बदलाव करें

ऐप्लिकेशन-लेवल और कॉन्टेंट-लेवल के कंपोज़ेबल, आपके ऐप्लिकेशन के लिए उपलब्ध डिसप्ले स्पेस का पूरा हिस्सा इस्तेमाल करते हैं. इस तरह के कंपोज़ेबल के लिए, बड़ी स्क्रीन पर अपने ऐप्लिकेशन के पूरे लेआउट को बदलना सही हो सकता है.

लेआउट तय करने के लिए, फ़िज़िकल हार्डवेयर की वैल्यू का इस्तेमाल न करें. ऐसा हो सकता है कि किसी डिवाइस के बारे में फ़ैसला लेने के लिए, आपको किसी तय वैल्यू (क्या डिवाइस टैबलेट है? क्या फ़िज़िकल स्क्रीन का कोई पहलू अनुपात है?), लेकिन इन सवालों के जवाब, आपके यूज़र इंटरफ़ेस (यूआई) के लिए उपलब्ध जगह का पता लगाने के लिए काम के नहीं हो सकते.

पहली इमेज. फ़ोन, फ़ोल्ड होने वाले डिवाइस, टैबलेट, और लैपटॉप के साइज़, डाइमेंशन या कॉन्फ़िगरेशन

टैबलेट पर, कोई ऐप्लिकेशन मल्टी-विंडो मोड में चल रहा हो सकता है. इसका मतलब है कि ऐप्लिकेशन, स्क्रीन को किसी दूसरे ऐप्लिकेशन के साथ स्प्लिट कर रहा हो सकता है. डेस्कटॉप विंडोविंग मोड या ChromeOS पर, कोई ऐप्लिकेशन साइज़ बदलने वाली विंडो में हो सकता है. फ़ोल्ड किए जा सकने वाले डिवाइस की तरह, एक से ज़्यादा फ़िज़िकल स्क्रीन भी हो सकती हैं. इन सभी मामलों में, कॉन्टेंट को कैसे दिखाया जाए, यह तय करने के लिए डिवाइस की स्क्रीन का साइज़ मायने नहीं रखता.

इसके बजाय, Jetpack WindowManager लाइब्रेरी से मिली मौजूदा विंडो मेट्रिक के आधार पर, यह तय करें कि आपके ऐप्लिकेशन को स्क्रीन का कितना हिस्सा असाइन किया गया है. Compose ऐप्लिकेशन में WindowManager का इस्तेमाल करने का उदाहरण देखने के लिए, JetNews का सैंपल देखें.

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

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

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

@Composable
fun MyApp(
    windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass
) {
    // Decide whether to show the top app bar based on window size class.
    val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND)

    // MyScreen logic is based on the showTopAppBar boolean flag.
    MyScreen(
        showTopAppBar = showTopAppBar,
        /* ... */
    )
}

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

नेस्ट किए गए कंपोज़ेबल को फिर से इस्तेमाल किया जा सकता है

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

मान लें कि एक नेस्ट किए गए कंपोज़ेबल में list-detail layout लागू किया गया है. यह एक या दो पैन अगल-बगल दिखा सकता है:

एक ऐप्लिकेशन में, दाएं-बाएं दो पैन दिखाए गए हैं.
दूसरी इमेज. इस इमेज में, सूची की जानकारी वाला सामान्य लेआउट दिखाया गया है. 1 सूची वाला एरिया है और 2, जानकारी वाला एरिया है.

सूची-ब्यौरे का फ़ैसला, ऐप्लिकेशन के पूरे लेआउट का हिस्सा होना चाहिए. इसलिए, फ़ैसले को कॉन्टेंट-लेवल के कंपोज़ेबल से पास किया जाता है:

@Composable
fun AdaptivePane(
    showOnePane: Boolean,
    /* ... */
) {
    if (showOnePane) {
        OnePane(/* ... */)
    } else {
        TwoPane(/* ... */)
    }
}

अगर आपको किसी कंपोज़ेबल को डिसप्ले के लिए उपलब्ध जगह के हिसाब से, अपने लेआउट को स्वतंत्र रूप से बदलने की सुविधा चाहिए, तो क्या करें? उदाहरण के लिए, एक ऐसा कार्ड जो जगह उपलब्ध होने पर अतिरिक्त जानकारी दिखाता है. आपको उपलब्ध डिसप्ले साइज़ के आधार पर कुछ लॉजिक लागू करना है, लेकिन कौनसा साइज़?

तीसरी इमेज. छोटी चौड़ाई वाला कार्ड, जिसमें सिर्फ़ आइकॉन और टाइटल दिख रहा है. साथ ही, ज़्यादा चौड़ाई वाला कार्ड, जिसमें आइकॉन, टाइटल, और खास जानकारी दिख रही है.

डिवाइस की असल स्क्रीन के साइज़ का इस्तेमाल करने की कोशिश न करें. यह अलग-अलग तरह की स्क्रीन के लिए सटीक नहीं होगा. साथ ही, अगर ऐप्लिकेशन फ़ुलस्क्रीन पर नहीं है, तो भी यह सटीक नहीं होगा.

कंपोज़ेबल, कॉन्टेंट-लेवल का कंपोज़ेबल नहीं है. इसलिए, मौजूदा विंडो मेट्रिक का सीधे तौर पर इस्तेमाल न करें.

अगर कॉम्पोनेंट को पैडिंग (जैसे कि इंसर्ट) के साथ रखा जाता है या ऐप्लिकेशन में नेविगेशन रेल या ऐप्लिकेशन बार जैसे कॉम्पोनेंट शामिल होते हैं, तो कंपोज़ेबल के लिए उपलब्ध डिसप्ले स्पेस, ऐप्लिकेशन के लिए उपलब्ध कुल स्पेस से काफ़ी अलग हो सकता है.

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

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

  • अगर आपको यह बदलना है कि क्या दिखाया जाए, तो BoxWithConstraints का इस्तेमाल करें. यह एक बेहतर विकल्प है. BoxWithConstraints, मेज़रमेंट की सीमाएं तय करता है. इनका इस्तेमाल करके, डिसप्ले के लिए उपलब्ध जगह के आधार पर अलग-अलग कंपोज़ेबल को कॉल किया जा सकता है. हालांकि, ऐसा करने से कुछ समस्याएं आ सकती हैं. जैसे, BoxWithConstraints लेआउट फ़ेज़ तक कंपोज़िशन को टाल देता है. ऐसा तब होता है, जब इन शर्तों के बारे में पता चलता है. इससे लेआउट के दौरान ज़्यादा काम करना पड़ता है.

@Composable
fun Card(/* ... */) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(/* ... */)
                Title(/* ... */)
            }
        } else {
            Row {
                Column {
                    Title(/* ... */)
                    Description(/* ... */)
                }
                Image(/* ... */)
            }
        }
    }
}

अलग-अलग डिसप्ले साइज़ के लिए, सभी डेटा उपलब्ध कराना

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

हालांकि, ऐसा करने से एकतरफ़ा डेटा फ़्लो के सिद्धांत का उल्लंघन होता है. इस सिद्धांत के तहत, डेटा को ऊपर की ओर ले जाया जा सकता है और कंपोज़ेबल को दिया जा सकता है, ताकि वे उसे सही तरीके से रेंडर कर सकें. कंपोज़ेबल को इतना डेटा दिया जाना चाहिए कि उसके पास हमेशा किसी भी डिसप्ले साइज़ के लिए ज़रूरत के मुताबिक कॉन्टेंट हो. भले ही, कॉन्टेंट का कुछ हिस्सा हमेशा इस्तेमाल न किया जाए.

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(description)
                }
                Image(imageUrl)
            }
        }
    }
}

Card उदाहरण के आधार पर, ध्यान दें कि description को हमेशा Card में पास किया जाता है. description का इस्तेमाल सिर्फ़ तब किया जाता है, जब चौड़ाई के हिसाब से उसे दिखाया जा सकता हो. हालांकि, Card के लिए हमेशा description की ज़रूरत होती है. भले ही, चौड़ाई कितनी भी हो.

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

इस सिद्धांत की मदद से, लेआउट में बदलाव होने पर भी स्थिति को बनाए रखा जा सकता है. डिसप्ले के सभी साइज़ में इस्तेमाल न की जाने वाली जानकारी को ऊपर रखकर, लेआउट के साइज़ में बदलाव होने पर भी ऐप्लिकेशन की स्थिति को बनाए रखा जा सकता है.

उदाहरण के लिए, showMore बूलियन फ़्लैग को ऊपर ले जाया जा सकता है, ताकि डिसप्ले का साइज़ बदलने पर लेआउट में कॉन्टेंट को छिपाने और दिखाने के बीच स्विच करने पर भी ऐप्लिकेशन की स्थिति बनी रहे:

@Composable
fun Card(
    imageUrl: String,
    title: String,
    description: String
) {
    var showMore by remember { mutableStateOf(false) }

    BoxWithConstraints {
        if (maxWidth < 400.dp) {
            Column {
                Image(imageUrl)
                Title(title)
            }
        } else {
            Row {
                Column {
                    Title(title)
                    Description(
                        description = description,
                        showMore = showMore,
                        onShowMoreToggled = { newValue ->
                            showMore = newValue
                        }
                    )
                }
                Image(imageUrl)
            }
        }
    }
}

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

Compose में अडैप्टिव लेआउट के बारे में ज़्यादा जानने के लिए, यहां दिए गए संसाधन देखें:

ऐप्लिकेशन के सैंपल

  • CanonicalLayouts, डिज़ाइन पैटर्न का एक ऐसा कलेक्शन है जो बड़ी स्क्रीन पर बेहतरीन उपयोगकर्ता अनुभव देता है
  • JetNews से पता चलता है कि किसी ऐसे ऐप्लिकेशन को कैसे डिज़ाइन किया जाए जो उपलब्ध डिसप्ले स्पेस का इस्तेमाल करने के लिए, अपने यूज़र इंटरफ़ेस (यूआई) को अडैप्ट करता है
  • Reply, मोबाइल, टैबलेट, और फ़ोल्ड किए जा सकने वाले डिवाइसों के लिए एक अडैप्टिव सैंपल है
  • Now in Android एक ऐसा ऐप्लिकेशन है जो अलग-अलग डिसप्ले साइज़ के साथ काम करने के लिए, अडैप्टिव लेआउट का इस्तेमाल करता है

वीडियो