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

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

ज़्यादा से ज़्यादा डिसप्ले साइज़ के साथ काम करने के लिए, अपने ऐप्लिकेशन के लेआउट को रिस्पॉन्सिव और अडैप्टिव बनाएं. इससे, आपका ऐप्लिकेशन अलग-अलग डिवाइस की स्क्रीन या मल्टी-विंडो मोड में अलग-अलग ऐप्लिकेशन विंडो के साथ काम कर पाएगा. रिस्पॉन्सिव/अडैप्टिव लेआउट, डिसप्ले के साइज़ के हिसाब से ऑप्टिमाइज़ किए गए होते हैं. इससे लोगों को बेहतर अनुभव मिलता है. साथ ही, आपका ऐप्लिकेशन फ़ोन, टैबलेट, फ़ोल्ड किए जा सकने वाले डिवाइसों, और 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,
        /* ... */
    )
}

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

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

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

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

एक ऐप्लिकेशन, जिसमें दो पैनल अगल-बगल दिख रहे हैं.
दूसरी इमेज. ऐप्लिकेशन में सूची की जानकारी वाला सामान्य लेआउट दिख रहा है—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 एक ऐसा ऐप्लिकेशन है जो अलग-अलग डिसप्ले साइज़ के साथ काम करने के लिए, अडैप्टिव लेआउट का इस्तेमाल करता है.

वीडियो