ناوبری تعامل کاربر با رابط کاربری برنامه برای دسترسی به مقصد محتوا است. اصول ناوبری اندروید دستورالعمل هایی را ارائه می دهد که به شما کمک می کند تا ناوبری برنامه های سازگار و بصری ایجاد کنید.
رابطهای کاربری واکنشگرا/تطبیقی، مقصدهای محتوای واکنشگرا را ارائه میکنند و اغلب شامل انواع مختلفی از عناصر ناوبری در پاسخ به تغییرات اندازه نمایشگر میشوند - به عنوان مثال، یک نوار ناوبری پایین در نمایشگرهای کوچک، یک ریل ناوبری در نمایشگرهای با اندازه متوسط، یا یک کشوی ناوبری مداوم در بزرگ نمایشگرها - اما رابطهای کاربری پاسخگو/تطبیقی همچنان باید با اصول ناوبری مطابقت داشته باشند.
مولفه Jetpack Navigation اصول ناوبری را پیاده سازی می کند و توسعه برنامه ها را با رابط های کاربری پاسخگو/تطبیقی تسهیل می کند.
ناوبری UI پاسخگو
اندازه پنجره نمایش اشغال شده توسط یک برنامه بر ارگونومی و قابلیت استفاده تأثیر می گذارد. کلاسهای اندازه پنجره به شما امکان میدهند عناصر ناوبری مناسب (مانند میلههای ناوبری، ریلها یا کشوها) را تعیین کنید و آنها را در جایی قرار دهید که بیشتر در دسترس کاربر هستند. در دستورالعملهای طرحبندی Material Design، عناصر ناوبری فضای ثابتی را در لبه جلویی نمایشگر اشغال میکنند و زمانی که عرض برنامه فشرده است میتوانند به لبه پایین حرکت کنند. انتخاب عناصر ناوبری شما تا حد زیادی به اندازه پنجره برنامه و تعداد مواردی که عنصر باید نگه دارد بستگی دارد.
کلاس اندازه پنجره | چند مورد | بسیاری از موارد |
---|---|---|
عرض فشرده | نوار ناوبری پایین | کشوی ناوبری (لبه جلو یا پایین) |
عرض متوسط | ریل ناوبری | کشوی ناوبری (لبه پیشرو) |
عرض منبسط شده | ریل ناوبری | کشوی ناوبری مداوم (لبه پیشرو) |
فایل های منبع چیدمان را می توان با نقاط شکست کلاس اندازه پنجره برای استفاده از عناصر ناوبری مختلف برای ابعاد مختلف نمایش واجد شرایط دانست.
<!-- 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
بستگی دارد.
در ساده ترین حالت، هر مقصد ناوبری یک قطعه واحد با حالت رابط کاربری کاملاً ایزوله است. و بنابراین، هر قطعه میتواند از خصوصیت viewModels()
برای بدست آوردن ViewModel
با محدوده آن قطعه استفاده کند.
برای به اشتراک گذاشتن حالت رابط کاربری بین فرگمنتها، ViewModel
با فراخوانی activityViewModels()
در فرگمنتها در محدوده فعالیت قرار دهید (معادل Activity
فقط viewModels()
است). این به فعالیت و هر قطعه ای که به آن متصل می شود اجازه می دهد تا نمونه ViewModel
را به اشتراک بگذارد. با این حال، در یک معماری تک فعالیتی، این محدوده ViewModel
به طور موثر تا زمانی که برنامه دوام می آورد، بنابراین ViewModel
در حافظه باقی می ماند حتی اگر هیچ قطعه ای از آن استفاده نکند.
فرض کنید نمودار ناوبری شما دارای دنباله ای از مقاصد قطعه است که یک جریان پرداخت را نشان می دهد و وضعیت فعلی کل تجربه پرداخت در یک ViewModel
است که بین قطعات به اشتراک گذاشته شده است. محدوده ViewModel
برای فعالیت نه تنها بسیار گسترده است، بلکه در واقع یک مشکل دیگر را آشکار می کند: اگر کاربر برای یک سفارش، جریان پرداخت را طی کند، و سپس دوباره آن را برای سفارش دوم مرور کند، هر دو سفارش از یک نمونه از پرداخت استفاده می کنند. ViewModel
. قبل از تسویه حساب سفارش دوم، باید داده های سفارش اول را به صورت دستی پاک کنید. هر گونه اشتباه می تواند برای کاربر گران تمام شود.
در عوض، ViewModel
را به یک نمودار ناوبری در NavController
فعلی محدود کنید. یک نمودار ناوبری تودرتو ایجاد کنید تا مقاصدی را که بخشی از جریان پرداخت هستند، محصور کنید. سپس در هر یک از آن مقاصد قطعه، از نماینده ویژگی navGraphViewModels()
استفاده کنید و شناسه گراف پیمایش را برای بدست آوردن ViewModel
مشترک ارسال کنید. این تضمین میکند که وقتی کاربر از جریان پرداخت خارج میشود و نمودار پیمایش تودرتو خارج از محدوده است، نمونه مربوطه ViewModel
کنار گذاشته میشود و برای پرداخت بعدی استفاده نمیشود.
دامنه | نماینده اموال | می تواند ViewModel با |
---|---|---|
قطعه | Fragment.viewModels() | فقط قطعه |
فعالیت | Activity.viewModels() یا Fragment.activityViewModels() | فعالیت و تمام قطعات متصل به آن |
نمودار ناوبری | Fragment.navGraphViewModels() | همه قطعات در یک نمودار ناوبری یکسان |
توجه داشته باشید که اگر از یک میزبان ناوبری تودرتو استفاده میکنید (به بخش میزبان ناوبری تودرتو مراجعه کنید)، مقصدهای موجود در آن میزبان نمیتوانند هنگام استفاده از navGraphViewModels()
نمونههای ViewModel
با مقصدهای خارج از میزبان به اشتراک بگذارند، زیرا نمودارها متصل نیستند. در این حالت می توانید به جای آن از محدوده فعالیت استفاده کنید.