रिस्पॉन्सिव नेविगेशन बनाना

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

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

Jetpack नेविगेशन कॉम्पोनेंट, नेविगेशन के सिद्धांतों को लागू करता है और रिस्पॉन्सिव/अडैप्टिव यूज़र इंटरफ़ेस (यूआई) वाले ऐप्लिकेशन को डेवलप करने में मदद करता है.

पहला डायग्राम. नेविगेशन ड्रॉअर, रेल, और बॉटम बार के साथ बड़े, मीडियम, और कॉम्पैक्ट डिसप्ले.

रिस्पॉन्सिव यूज़र इंटरफ़ेस (यूआई) नेविगेशन

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

विंडो साइज़ क्लास कुछ आइटम कई आइटम
कॉम्पैक्ट चौड़ाई सबसे नीचे मौजूद नेविगेशन बार नेविगेशन पैनल (लीडिंग एज या सबसे नीचे)
मीडियम चौड़ाई नेविगेशन रेल नेविगेशन पैनल (लीडिंग एज)
बढ़ाई गई चौड़ाई नेविगेशन रेल हमेशा दिखने वाला नेविगेशन पैनल (लीडिंग एज)

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

<!-- res/layout/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

<!-- res/layout-w600dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigationrail.NavigationRailView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

<!-- res/layout-w1240dp/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.navigation.NavigationView
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />

    <!-- Content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

रिस्पॉन्सिव कॉन्टेंट डेस्टिनेशन

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

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

विंडो के साइज़ में बदलाव होने पर, नेविगेशन नहीं होना चाहिए. सिर्फ़ अलग-अलग विंडो साइज़ के हिसाब से कॉन्टेंट डेस्टिनेशन न बनाएं. उदाहरण के लिए, फ़ोल्ड किए जा सकने वाले डिवाइस की अलग-अलग स्क्रीन के लिए, अलग-अलग कॉन्टेंट डेस्टिनेशन न बनाएं.

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

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

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

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

वैकल्पिक लेआउट वाले कॉन्टेंट डेस्टिनेशन

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

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

SlidingPaneLayout की मदद से, एक ऐसा नेविगेशन डेस्टिनेशन बनाया जा सकता है जो बड़ी स्क्रीन पर, दो कॉन्टेंट पैनल को एक साथ दिखाता है. हालांकि, यह एक बार में एक ही पैनल को छोटी स्क्रीन पर दिखाता है, जैसे कि सामान्य फ़ोन पर.

<!-- Single destination for list and detail. -->

<navigation ...>

    <!-- Fragment that implements SlidingPaneLayout. -->
    <fragment
        android:id="@+id/article_two_pane"
        android:name="com.example.app.ListDetailTwoPaneFragment" />

    <!-- Other destinations... -->
</navigation>

SlidingPaneLayout का इस्तेमाल करके, सूची के ब्यौरे वाला लेआउट लागू करने के बारे में जानने के लिए, दो पैनल वाला लेआउट बनाना देखें.

एक नेविगेशन ग्राफ़

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

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

नेस्ट किया गया नेविगेशन होस्ट

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

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

<!-- layout/two_pane_fragment.xml -->

<androidx.slidingpanelayout.widget.SlidingPaneLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/sliding_pane_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/list_pane"
        android:layout_width="280dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"/>

    <!-- Detail pane is a nested navigation host. Its graph is not connected
         to the main graph that contains the two_pane_fragment destination. -->
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/detail_pane"
        android:layout_width="300dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/detail_pane_nav_graph" />
</androidx.slidingpanelayout.widget.SlidingPaneLayout>

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

ज़्यादा जानकारी के लिए, नेस्ट किए गए नेविगेशन ग्राफ़ देखें.

सेव की गई स्थिति

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

साइज़ में किए गए बदलावों को पहले जैसा किया जा सकता हो. उदाहरण के लिए, जब उपयोगकर्ता डिवाइस को घुमाता है और फिर उसे वापस घुमाता है.

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

ViewModel के स्कोप

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

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

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

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

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

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

दायरा प्रॉपर्टी का ऐक्सेस देना ViewModel को
फ़्रैगमेंट Fragment.viewModels() सिर्फ़ फ़्रैगमेंट
गतिविधि Activity.viewModels() या Fragment.activityViewModels() गतिविधि और उससे जुड़े सभी फ़्रैगमेंट
नेविगेशन ग्राफ़ Fragment.navGraphViewModels() एक ही नेविगेशन ग्राफ़ में मौजूद सभी फ़्रैगमेंट

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

अन्य संसाधन