1. परिचय
Android डिवाइसों का नेटवर्क हमेशा बदलता रहता है. Android ऐप्लिकेशन, आज के समय में पहले से ज़्यादा डिवाइसों पर काम करते हैं. जैसे, बिल्ट-इन हार्डवेयर कीबोर्ड वाले डिवाइस, फ़्लिप किए जा सकने वाले डिवाइस, फ़ोल्ड किए जा सकने वाले डिवाइस, टैबलेट, और फ़्री-फ़ॉर्म में साइज़ बदलने वाली विंडो.
यह डेवलपर के लिए अच्छी खबर है. हालांकि, ऐप्लिकेशन को इस्तेमाल करने से जुड़ी उम्मीदों को पूरा करने के लिए, कुछ ऐप्लिकेशन ऑप्टिमाइज़ेशन ज़रूरी हैं. साथ ही, अलग-अलग स्क्रीन साइज़ पर लोगों को बेहतरीन अनुभव देने के लिए भी ऐसा करना ज़रूरी है. हर नए डिवाइस को एक-एक करके टारगेट करने के बजाय, रिस्पॉन्सिव/अडैप्टिव यूज़र इंटरफ़ेस (यूआई) और लचीला आर्किटेक्चर का इस्तेमाल करें. इससे आपके ऐप्लिकेशन को मौजूदा और आने वाले समय के उपयोगकर्ताओं के लिए, हर डिवाइस पर बेहतर तरीके से काम करने और दिखने में मदद मिलेगी. चाहे डिवाइस का साइज़ और शेप कैसा भी हो!
Android के ऐसे एनवायरमेंट का इस्तेमाल किया जा सकता है जिनमें ऐप्लिकेशन को किसी भी साइज़ में बदला जा सकता है. इससे, रिस्पॉन्सिव/अडैप्टिव यूज़र इंटरफ़ेस (यूआई) की परफ़ॉर्मेंस की जांच की जा सकती है. इससे यह पता चलता है कि यूआई, किसी भी डिवाइस पर काम करने के लिए तैयार है या नहीं. इस कोडलैब में, आपको ऐप्लिकेशन का साइज़ बदलने के असर के बारे में बताया जाएगा. साथ ही, ऐप्लिकेशन का साइज़ आसानी से और बेहतर तरीके से बदलने के लिए, कुछ सबसे सही तरीकों को लागू करने के बारे में भी बताया जाएगा.
आपको क्या बनाना है
इसमें आपको फ़्री-फ़ॉर्म रीसाइज़िंग के असर के बारे में बताया जाएगा. साथ ही, रीसाइज़िंग के सबसे सही तरीके दिखाने के लिए, Android ऐप्लिकेशन को ऑप्टिमाइज़ किया जाएगा. आपका ऐप्लिकेशन:
मेनिफ़ेस्ट, डिवाइस के साथ काम करता हो
- उन पाबंदियों को हटाएं जिनकी वजह से, ऐप्लिकेशन का साइज़ बदलने में समस्या आ रही है
साइज़ बदलने पर स्थिति बनाए रखें
- rememberSaveable का इस्तेमाल करके, साइज़ बदलने पर यूज़र इंटरफ़ेस (यूआई) की स्थिति बनाए रखता है
- यूज़र इंटरफ़ेस (यूआई) को शुरू करने के लिए, बैकग्राउंड में होने वाले काम को बेवजह दोहराने से बचें
आपको किन चीज़ों की ज़रूरत होगी
- Android ऐप्लिकेशन बनाने की बुनियादी जानकारी
- Compose में ViewModel और State के बारे में जानकारी
- ऐसा टेस्ट डिवाइस जो फ़्री-फ़ॉर्म विंडो का साइज़ बदलने की सुविधा के साथ काम करता हो. जैसे, इनमें से कोई एक डिवाइस:
- ADB सेटअप वाला Chromebook
- ऐसा टैबलेट जिस पर Samsung DeX Mode या Productivity Mode काम करता हो
- Android Studio में डेस्कटॉप Android वर्चुअल डिवाइस एम्युलेटर
अगर आपको इस कोडलैब को पूरा करते समय कोई समस्या (कोड में गड़बड़ियां, व्याकरण की गलतियां, शब्दों का सही इस्तेमाल न होना वगैरह) आती है, तो कृपया कोडलैब के सबसे नीचे बाएं कोने में मौजूद गड़बड़ी की शिकायत करें लिंक पर क्लिक करके समस्या की शिकायत करें.
2. शुरू करें
GitHub से रिपॉज़िटरी को क्लोन करें.
git clone https://github.com/android/large-screen-codelabs/
...या रिपॉज़िटरी की ZIP फ़ाइल डाउनलोड करें और उसे एक्सट्रैक्ट करें
प्रोजेक्ट इंपोर्ट करें
- Android Studio खोलना
- प्रोजेक्ट इंपोर्ट करें या फ़ाइल->नया->प्रोजेक्ट इंपोर्ट करें चुनें
- उस जगह पर जाएं जहां आपने प्रोजेक्ट को क्लोन या एक्सट्रैक्ट किया है
- बदला गया साइज़ फ़ोल्डर खोलें.
- प्रोजेक्ट को start फ़ोल्डर में खोलें. इसमें स्टार्टर कोड होता है.
ऐप्लिकेशन आज़माएं
- ऐप्लिकेशन बनाना और उसे चलाना
- ऐप्लिकेशन का साइज़ बदलकर देखें
आपकी क्या राय है?
आपके टेस्ट डिवाइस पर, ऐप्लिकेशन के साथ काम करने की सुविधा के आधार पर, आपको शायद यह पता चला हो कि उपयोगकर्ता अनुभव अच्छा नहीं है. ऐप्लिकेशन का साइज़ नहीं बदला जा सकता और यह शुरुआती आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) में ही दिखता है. क्या हो रहा है?
मेनिफ़ेस्ट से जुड़ी पाबंदियां
अगर ऐप्लिकेशन की AndroidManifest.xml फ़ाइल में देखा जाए, तो आपको पता चलेगा कि कुछ पाबंदियां जोड़ी गई हैं. इनकी वजह से, फ़्री-फ़ॉर्म विंडो का साइज़ बदलने वाले एनवायरमेंट में हमारा ऐप्लिकेशन ठीक से काम नहीं कर पा रहा है.
AndroidManifest.xml
android:maxAspectRatio="1.4"
android:resizeableActivity="false"
android:screenOrientation="portrait">
अपनी मेनिफ़ेस्ट फ़ाइल से इन तीन लाइनों को हटाकर देखें. इसके बाद, ऐप्लिकेशन को फिर से बनाएं और अपने टेस्ट डिवाइस पर इसे फिर से आज़माएं. आपको दिखेगा कि अब ऐप्लिकेशन को किसी भी साइज़ में बदलने पर कोई पाबंदी नहीं है. इस तरह की पाबंदियों को मेनिफ़ेस्ट से हटाना, फ़्री-फ़ॉर्म विंडो का साइज़ बदलने के लिए अपने ऐप्लिकेशन को ऑप्टिमाइज़ करने का एक अहम चरण है.
3. रीसाइज़ करने से जुड़े कॉन्फ़िगरेशन में बदलाव
आपके ऐप्लिकेशन की विंडो का साइज़ बदलने पर, आपके ऐप्लिकेशन का कॉन्फ़िगरेशन अपडेट हो जाता है. इन अपडेट का असर आपके ऐप्लिकेशन पर पड़ेगा. इनके बारे में जानने और इनका अनुमान लगाने से, उपयोगकर्ताओं को बेहतर अनुभव देने में मदद मिल सकती है. सबसे अहम बदलाव, आपके ऐप्लिकेशन की विंडो की चौड़ाई और ऊंचाई में होते हैं. हालांकि, इन बदलावों का असर आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) और ओरिएंटेशन पर भी पड़ता है.
कॉन्फ़िगरेशन में हुए बदलावों को मॉनिटर करना
अगर आपको Android व्यू सिस्टम का इस्तेमाल करके बनाए गए किसी ऐप्लिकेशन में ये बदलाव होते हुए देखने हैं, तो View.onConfigurationChanged को बदला जा सकता है. Jetpack Compose में, हमारे पास LocalConfiguration.current को ऐक्सेस करने का विकल्प होता है. जब भी View.onConfigurationChanged को कॉल किया जाता है, तब यह अपने-आप अपडेट हो जाता है.
अपने सैंपल ऐप्लिकेशन में कॉन्फ़िगरेशन में हुए इन बदलावों को देखने के लिए, अपने ऐप्लिकेशन में एक कंपोज़ेबल जोड़ें. यह कंपोज़ेबल, LocalConfiguration.current से वैल्यू दिखाता है. इसके अलावा, ऐसा कंपोज़ेबल इस्तेमाल करके एक नया सैंपल प्रोजेक्ट बनाया जा सकता है. इन्हें देखने के लिए, यूज़र इंटरफ़ेस (यूआई) का उदाहरण कुछ ऐसा होगा:
val configuration = LocalConfiguration.current
val isPortrait = configuration.orientation ==
Configuration.ORIENTATION_PORTRAIT
val screenLayoutSize =
when (configuration.screenLayout and
Configuration.SCREENLAYOUT_SIZE_MASK) {
SCREENLAYOUT_SIZE_SMALL -> "SCREENLAYOUT_SIZE_SMALL"
SCREENLAYOUT_SIZE_NORMAL -> "SCREENLAYOUT_SIZE_NORMAL"
SCREENLAYOUT_SIZE_LARGE -> "SCREENLAYOUT_SIZE_LARGE"
SCREENLAYOUT_SIZE_XLARGE -> "SCREENLAYOUT_SIZE_XLARGE"
else -> "undefined value"
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
Text("screenWidthDp: ${configuration.screenWidthDp}")
Text("screenHeightDp: ${configuration.screenHeightDp}")
Text("smallestScreenWidthDp: ${configuration.smallestScreenWidthDp}")
Text("orientation: ${if (isPortrait) "portrait" else "landscape"}")
Text("screenLayout SIZE: $screenLayoutSize")
}
observing-configuration-changes प्रोजेक्ट फ़ोल्डर में, लागू करने का उदाहरण देखा जा सकता है. इसे अपने ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में जोड़ें. इसके बाद, इसे अपने टेस्ट डिवाइस पर चलाएँ. साथ ही, देखें कि ऐप्लिकेशन के कॉन्फ़िगरेशन में बदलाव होने पर, यूज़र इंटरफ़ेस (यूआई) कैसे अपडेट होता है.

ऐप्लिकेशन के कॉन्फ़िगरेशन में किए गए इन बदलावों की मदद से, यह आसानी से देखा जा सकता है कि स्प्लिट स्क्रीन मोड में छोटे हैंडसेट पर ऐप्लिकेशन कैसा दिखेगा और फ़ुल स्क्रीन मोड में टैबलेट या डेस्कटॉप पर कैसा दिखेगा. इससे न सिर्फ़ अलग-अलग स्क्रीन पर अपने ऐप्लिकेशन के लेआउट की जांच की जा सकती है, बल्कि यह भी देखा जा सकता है कि आपका ऐप्लिकेशन, कॉन्फ़िगरेशन में तेज़ी से होने वाले बदलावों को कितनी अच्छी तरह से मैनेज कर सकता है.
4. गतिविधि की लाइफ़साइकल के इवेंट लॉग करना
आपके ऐप्लिकेशन के लिए, फ़्री-फ़ॉर्म विंडो का साइज़ बदलने का एक और मतलब यह है कि आपके ऐप्लिकेशन के लिए, Activity लाइफ़साइकल में कई बदलाव होंगे. इन बदलावों को रीयल टाइम में देखने के लिए, अपनी onCreate विधि में लाइफ़साइकल ऑब्ज़र्वर जोड़ें. साथ ही, onStateChanged को बदलकर हर नए लाइफ़साइकल इवेंट को लॉग करें.
lifecycle.addObserver(object : LifecycleEventObserver {
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
Log.d("resizing-codelab-lifecycle", "$event was called")
}
})
लॉग इन करने के बाद, अपने टेस्ट डिवाइस पर ऐप्लिकेशन को फिर से चलाएं. साथ ही, ऐप्लिकेशन को छोटा करने और उसे फिर से फ़ोरग्राउंड में लाने के दौरान, logcat को देखें.
देखें कि ऐप्लिकेशन को छोटा करने पर, वह रुक जाता है. इसके बाद, उसे फ़ोरग्राउंड में लाने पर फिर से शुरू हो जाता है. इसका असर आपके ऐप्लिकेशन पर पड़ता है. इसके बारे में, आपको इस कोडलैब के आने वाले सेक्शन में पता चलेगा. इस सेक्शन में, ऐप्लिकेशन को एक डिवाइस से दूसरे डिवाइस पर इस्तेमाल करने की सुविधा पर फ़ोकस किया गया है.

अब Logcat देखें. इससे पता चलेगा कि ऐप्लिकेशन को सबसे छोटे साइज़ से सबसे बड़े साइज़ में बदलते समय, गतिविधि के लाइफ़साइकल के कौनसे कॉलबैक फ़ंक्शन कॉल किए जाते हैं
आपके टेस्ट डिवाइस के हिसाब से, आपको अलग-अलग व्यवहार दिख सकते हैं. हालांकि, आपने शायद यह देखा होगा कि जब आपके ऐप्लिकेशन की विंडो का साइज़ काफ़ी बदल जाता है, तब आपकी गतिविधि बंद हो जाती है और फिर से शुरू होती है. हालांकि, ऐसा तब नहीं होता, जब विंडो का साइज़ थोड़ा बदलता है. ऐसा इसलिए है, क्योंकि API 24 और इसके बाद के वर्शन में, सिर्फ़ साइज़ में बड़े बदलाव होने पर ही Activity फिर से बनाया जाता है.
आपने फ़्री-फ़ॉर्म विंडोइंग एनवायरमेंट में कॉन्फ़िगरेशन में होने वाले कुछ सामान्य बदलावों के बारे में जाना. हालांकि, आपको अन्य बदलावों के बारे में भी पता होना चाहिए. उदाहरण के लिए, अगर आपने अपने टेस्ट डिवाइस से कोई बाहरी मॉनिटर कनेक्ट किया है, तो आपको दिखेगा कि डिसप्ले डेंसिटी जैसे कॉन्फ़िगरेशन में हुए बदलावों को ध्यान में रखने के लिए, आपके Activity को डिस्ट्रॉय करके फिर से बनाया गया है.
कॉन्फ़िगरेशन में बदलाव करने से जुड़ी कुछ मुश्किलों को कम करने के लिए, ज़्यादा लेवल वाले एपीआई का इस्तेमाल करें. जैसे, अडैप्टिव यूज़र इंटरफ़ेस (यूआई) लागू करने के लिए WindowSizeClass. (अलग-अलग स्क्रीन साइज़ के साथ काम करना लेख भी पढ़ें.)
5. निरंतरता - साइज़ बदलने पर, कंपोज़ेबल की इंटरनल स्थिति को बनाए रखना
पिछले सेक्शन में, आपने कॉन्फ़िगरेशन में होने वाले कुछ ऐसे बदलावों के बारे में जाना जो फ़्री-फ़ॉर्म विंडो का साइज़ बदलने की सुविधा के साथ काम करने वाले ऐप्लिकेशन में हो सकते हैं. इस सेक्शन में, आपको इन बदलावों के दौरान अपने ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) की स्थिति को बनाए रखना होगा.
सबसे पहले, NavigationDrawerHeader कंपोज़ेबल फ़ंक्शन (ReplyHomeScreen.kt में मौजूद) को बड़ा करें, ताकि क्लिक करने पर ईमेल पता दिखे.
@Composable
private fun NavigationDrawerHeader(
modifier: Modifier = Modifier
) {
var showDetails by remember { mutableStateOf(false) }
Column(
modifier = modifier.clickable {
showDetails = !showDetails
}
) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
ReplyLogo(
modifier = Modifier
.size(dimensionResource(R.dimen.reply_logo_size))
)
ReplyProfileImage(
drawableResource = LocalAccountsDataProvider
.userAccount.avatar,
description = stringResource(id = R.string.profile),
modifier = Modifier
.size(dimensionResource(R.dimen.profile_image_size))
)
}
AnimatedVisibility (showDetails) {
Text(
text = stringResource(id = LocalAccountsDataProvider
.userAccount.email),
style = MaterialTheme.typography.labelMedium,
modifier = Modifier
.padding(
start = dimensionResource(
R.dimen.drawer_padding_header),
end = dimensionResource(
R.dimen.drawer_padding_header),
bottom = dimensionResource(
R.dimen.drawer_padding_header)
),
)
}
}
}
अपने ऐप्लिकेशन में बड़ा किया जा सकने वाला हेडर जोड़ने के बाद,
- अपने टेस्ट डिवाइस पर ऐप्लिकेशन चलाएं
- हेडर को बड़ा करने के लिए, उस पर टैप करें
- विंडो का साइज़ बदलकर देखें
आपको दिखेगा कि हेडर का साइज़ काफ़ी कम होने पर, वह अपनी स्थिति में नहीं रहता.

यूज़र इंटरफ़ेस (यूआई) की स्थिति इसलिए नहीं दिख रही है, क्योंकि remember आपको फिर से कंपोज़ करने पर स्थिति बनाए रखने में मदद करता है. हालांकि, यह गतिविधि या प्रोसेस को फिर से बनाने पर स्थिति बनाए रखने में मदद नहीं करता. स्टेट होइस्टिंग का इस्तेमाल करना आम बात है. इसमें स्टेट को कंपोज़ेबल के कॉलर में ले जाया जाता है, ताकि कंपोज़ेबल को स्टेटलेस बनाया जा सके. इससे इस समस्या से पूरी तरह से बचा जा सकता है. हालांकि, कंपोज़ेबल फ़ंक्शन के अंदर यूज़र इंटरफ़ेस (यूआई) एलिमेंट की स्थिति को बनाए रखने के लिए, remember का इस्तेमाल किया जा सकता है.
इन समस्याओं को हल करने के लिए, remember को rememberSaveable से बदलें. ऐसा इसलिए होता है, क्योंकि rememberSaveable, याद की गई वैल्यू को savedInstanceState में सेव और रीस्टोर करता है. remember को rememberSaveable पर सेट करें. इसके बाद, टेस्ट डिवाइस पर अपना ऐप्लिकेशन चलाएं और ऐप्लिकेशन का साइज़ फिर से बदलने की कोशिश करें. आपको दिखेगा कि साइज़ बदलने के दौरान, एक्सपैंड किए जा सकने वाले हेडर का स्टेटस वैसा ही बना रहता है जैसा होना चाहिए.
6. बैकग्राउंड में होने वाले काम को बार-बार करने से बचना
आपने देखा कि rememberSaveable का इस्तेमाल करके, कॉन्फ़िगरेशन में होने वाले बदलावों के दौरान कंपोज़ेबल की इंटरनल यूज़र इंटरफ़ेस (यूआई) स्थिति को कैसे बनाए रखा जा सकता है. विंडो का साइज़ बदलने की सुविधा की वजह से, कॉन्फ़िगरेशन में अक्सर बदलाव हो सकते हैं. हालांकि, किसी ऐप्लिकेशन को अक्सर यूज़र इंटरफ़ेस (यूआई) की स्थिति और लॉजिक को कंपोज़ेबल से अलग रखना चाहिए. स्क्रीन का साइज़ बदलने के दौरान स्टेट को बनाए रखने के लिए, स्टेट का मालिकाना हक ViewModel को ट्रांसफ़र करना सबसे सही तरीका है. अपने स्टेट को ViewModel में ले जाते समय, आपको लंबे समय तक चलने वाले बैकग्राउंड प्रोसेस से जुड़ी समस्याएं आ सकती हैं. जैसे, फ़ाइल सिस्टम को ज़्यादा ऐक्सेस करना या नेटवर्क कॉल करना. ये समस्याएं, आपकी स्क्रीन को शुरू करने के लिए ज़रूरी होती हैं.
आपको किस तरह की समस्याएं आ सकती हैं, इसका उदाहरण देखने के लिए, ReplyViewModel में मौजूद initializeUIState तरीके में लॉग स्टेटमेंट जोड़ें.
fun initializeUIState() {
Log.d("resizing-codelab", "initializeUIState() called in the viewmodel")
val mailboxes: Map<MailboxType, List<Email>> =
LocalEmailsDataProvider.allEmails.groupBy { it.mailbox }
_uiState.value =
ReplyUiState(
mailboxes = mailboxes,
currentSelectedEmail = mailboxes[MailboxType.Inbox]?.get(0)
?: LocalEmailsDataProvider.defaultEmail
)
}
अब अपने टेस्ट डिवाइस पर ऐप्लिकेशन चलाएं और ऐप्लिकेशन की विंडो का साइज़ कई बार बदलें.
Logcat देखने पर, आपको पता चलेगा कि आपका ऐप्लिकेशन, कई बार शुरू होने का तरीका दिखाता है. यह उस काम के लिए समस्या हो सकती है जिसे आपको यूज़र इंटरफ़ेस (यूआई) को शुरू करने के लिए सिर्फ़ एक बार चलाना है. नेटवर्क कॉल, फ़ाइल I/O या अन्य काम की वजह से, डिवाइस की परफ़ॉर्मेंस पर असर पड़ सकता है. साथ ही, इससे अन्य समस्याएं भी हो सकती हैं.
बैकग्राउंड में होने वाले गैर-ज़रूरी काम से बचने के लिए, अपनी गतिविधि के onCreate() तरीके से initializeUIState() को हटाने का अनुरोध करें. इसके बजाय, ViewModel के init तरीके में डेटा को शुरू करें. इससे यह पक्का होता है कि ReplyViewModel को पहली बार इंस्टैंटिएट करने पर, शुरू करने का तरीका सिर्फ़ एक बार चलता है:
init {
initializeUIState()
}
ऐप्लिकेशन को फिर से चलाकर देखें. आपको दिखेगा कि सिम्युलेट किया गया गैर-ज़रूरी टास्क सिर्फ़ एक बार चलता है. भले ही, आपने ऐप्लिकेशन की विंडो का साइज़ कितनी बार बदला हो. ऐसा इसलिए होता है, क्योंकि ViewModels, Activity की लाइफ़साइकल के बाद भी बने रहते हैं. ViewModel बनाते समय, शुरुआती कोड को सिर्फ़ एक बार चलाने से, हम इसे Activity के किसी भी रीक्रिएशन से अलग कर देते हैं. इससे बेवजह काम करने से बचा जा सकता है. अगर यह वाकई में महंगा सर्वर कॉल या यूज़र इंटरफ़ेस (यूआई) को शुरू करने के लिए भारी फ़ाइल I/O ऑपरेशन होता, तो आपको काफ़ी संसाधनों की बचत होती और उपयोगकर्ता अनुभव बेहतर होता.
7. बधाई हो!
आपने कर दिखाया! बहुत बढ़िया! आपने अब ChromeOS और मल्टी-विंडो, मल्टी-स्क्रीन वाले अन्य एनवायरमेंट पर Android ऐप्लिकेशन का साइज़ बदलने की सुविधा को बेहतर तरीके से चालू करने के लिए, कुछ सबसे सही तरीके लागू कर लिए हैं.
सोर्स कोड का सैंपल
GitHub से रिपॉज़िटरी का क्लोन बनाना
git clone https://github.com/android/large-screen-codelabs/
...या रिपॉज़िटरी की ZIP फ़ाइल डाउनलोड करें और उसे एक्सट्रैक्ट करें