يوفر مكون التنقل طرقًا لإنشاء عناصر تنقل معينة والتفاعل معها آليًا.
إنشاء NavHostFragment
يمكنك استخدام
NavHostFragment.create()
لإنشاء NavHostFragment
آليًا باستخدام مورد رسم بياني محدّد،
كما هو موضّح في المثال أدناه:
Kotlin
val finalHost = NavHostFragment.create(R.navigation.example_graph) supportFragmentManager.beginTransaction() .replace(R.id.nav_host, finalHost) .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true" .commit()
Java
NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph); getSupportFragmentManager().beginTransaction() .replace(R.id.nav_host, finalHost) .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true" .commit();
يُرجى العِلم أنّ سياسة setPrimaryNavigationFragment(finalHost)
تتيح لنظام NavHost
اعتراض الضغط على زر الرجوع في النظام. يمكنك أيضًا تنفيذ هذا الإجراء في ملف XML الخاص بـ NavHost
من خلال إضافة السمة app:defaultNavHost="true"
. إذا كنت تنفّذ
سلوكًا مخصّصًا لزر الرجوع
ولا تريد أن يضغط NavHost
على زر الرجوع، يمكنك تمرير
null
إلى setPrimaryNavigationFragment()
.
الإشارة إلى وجهة باستخدام NavBackStackEntry
بدءًا من الانتقال إلى الإصدار 2.2.0، يمكنك الحصول على مرجع إلى
NavBackStackEntry
لأي وجهة في حزمة التنقّل عن طريق استدعاء
NavController.getBackStackEntry()
،
وتمرير رقم تعريف وجهة لها. إذا كان المكدس الخلفي يحتوي على أكثر من نسخة واحدة
من الوجهة المحددة، تعرض getBackStackEntry()
أعلى مثيل
من المكدس.
تعرض سمة NavBackStackEntry
المعروضة
Lifecycle
و
ViewModelStore
و
SavedStateRegistry
على مستوى الوجهة. هذه العناصر صالحة طوال عمر الوجهة في الحزمة الخلفية. عندما تظهر الوجهة المرتبطة من الحزمة الخلفية، يتم محو Lifecycle
ولا يتم حفظ الحالة، ويتم محو أي عناصر ViewModel
.
توفّر لك هذه السمات Lifecycle
ومتجرًا لعناصر ViewModel
وفئات متوافقة مع
الحالة المحفوظة بغض النظر عن
نوع الوجهة التي تستخدمها. ويُعدّ ذلك مفيدًا بشكلٍ خاص عند العمل مع
أنواع الوجهات التي لا ترتبط تلقائيًا بسمة Lifecycle
،
مثل الوجهات المخصّصة.
على سبيل المثال، يمكنك الاطّلاع على Lifecycle
لـ NavBackStackEntry
تمامًا كما ستلاحظ Lifecycle
لجزء أو نشاط. بالإضافة إلى ذلك،
NavBackStackEntry
هو LifecycleOwner
، ما يعني أنّه يمكنك استخدامه عند
ملاحظة LiveData
أو مع مكوِّنات أخرى حسب مراحل نشاط الحياة، كما هو موضّح في
المثال التالي:
Kotlin
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
Java
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
يتم تعديل حالة مراحل النشاط تلقائيًا عند الاتصال بالرقم navigate()
.
يتم نقل حالات مراحل النشاط للوجهات التي لا تقع في أعلى الحزمة الخلفية من RESUMED
إلى STARTED
إذا كانت الوجهات لا تزال مرئية ضمن وجهة FloatingWindow
، مثل وجهة مربّع حوار، أو إلى STOPPED
بطريقة أخرى.
عرض نتيجة إلى الوجهة السابقة
في شريط التنقل 2.3 والإصدارات الأحدث، يتيح NavBackStackEntry
الوصول إلى
SavedStateHandle
.
SavedStateHandle
هي خريطة مفتاح/قيمة يمكن استخدامها لتخزين البيانات واستردادها. وتستمر هذه القيم حتى انتهاء العملية، بما في ذلك تغييرات التكوين، وتظل متاحة من خلال نفس الكائن. باستخدام السمة SavedStateHandle
المحددة، يمكنك الوصول إلى البيانات وتمريرها بين الوجهات.
هذه الطريقة مفيدة بشكل خاص كآلية لاسترجاع البيانات من وجهة بعد خروجها من المكدس.
لتمرير البيانات مرة أخرى إلى الوجهة "أ" من الوجهة "ب"، يجب أولاً
إعداد الوجهة "أ" للاستماع إلى نتيجة في SavedStateHandle
.
لإجراء ذلك، استرِد NavBackStackEntry
باستخدام واجهة برمجة تطبيقات getCurrentBackStackEntry()
، ثم observe
LiveData
المقدَّمة من SavedStateHandle
.
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val navController = findNavController(); // We use a String here, but any type that can be put in a Bundle is supported navController.currentBackStackEntry?.savedStateHandle?.getLiveData<String>("key")?.observe( viewLifecycleOwner) { result -> // Do something with the result. } }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { NavController navController = NavHostFragment.findNavController(this); // We use a String here, but any type that can be put in a Bundle is supported MutableLiveData<String> liveData = navController.getCurrentBackStackEntry() .getSavedStateHandle() .getLiveData("key"); liveData.observe(getViewLifecycleOwner(), new Observer<String>() { @Override public void onChanged(String s) { // Do something with the result. } }); }
في الوجهة "ب"، يجب set
النتيجة في SavedStateHandle
للوجهة أ باستخدام getPreviousBackStackEntry()
واجهة برمجة التطبيقات.
Kotlin
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Java
navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);
إذا أردت معالجة نتيجة واحدة فقط، يجب طلب الرمز
remove()
على SavedStateHandle
لمحو النتيجة. إذا لم تتم إزالة النتيجة، ستواصل LiveData
عرض النتيجة الأخيرة إلى أي حالات Observer
جديدة.
الاعتبارات الواجب مراعاتها عند استخدام وجهات مربّعات الحوار
عند navigate
إلى وجهة توفّر العرض الكامل
لـ NavHost
(مثل وجهة <fragment>
)، يتم إيقاف مراحل نشاط الوجهة السابقة، ما يمنع أي معاودة اتصال إلى LiveData
المقدَّمة من خلال SavedStateHandle
.
مع ذلك، عند الانتقال إلى
وجهة محادثة،
تظهر الوجهة السابقة أيضًا على الشاشة وبالتالي هي
STARTED
على الرغم من أنها ليست الوجهة الحالية. وهذا يعني أنّ المكالمات الواردة إلى
getCurrentBackStackEntry()
من خلال طرق مراحل النشاط، مثل
onViewCreated()
ستعرض NavBackStackEntry
من وجهة مربّع الحوار
بعد تغيير في الإعدادات أو إيقاف العملية وإعادة الترفيه (نظرًا لإعادة عرض
مربّع الحوار فوق الوجهة الأخرى). لذلك، يجب استخدام السمة
getBackStackEntry()
مع رقم تعريف وجهتك لضمان استخدام العلامة
NavBackStackEntry
الصحيحة دائمًا.
وهذا يعني أيضًا أنّه سيتم تشغيل أي Observer
تضبطه في النتيجة LiveData
حتى عندما لا تزال وجهات مربّع الحوار تظهر على الشاشة. إذا كنت تريد الاطّلاع فقط على النتيجة عند إغلاق وجهة مربّع الحوار وتصبح الوجهة الأساسية هي الوجهة الحالية، يمكنك ملاحظة Lifecycle
المرتبطة بـ NavBackStackEntry
واسترداد النتيجة فقط عندما تصبح RESUMED
.
Kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val navController = findNavController(); // After a configuration change or process death, the currentBackStackEntry // points to the dialog destination, so you must use getBackStackEntry() // with the specific ID of your destination to ensure we always // get the right NavBackStackEntry val navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment) // Create our observer and add it to the NavBackStackEntry's lifecycle val observer = LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_RESUME && navBackStackEntry.savedStateHandle.contains("key")) { val result = navBackStackEntry.savedStateHandle.get<String>("key"); // Do something with the result } } navBackStackEntry.lifecycle.addObserver(observer) // As addObserver() does not automatically remove the observer, we // call removeObserver() manually when the view lifecycle is destroyed viewLifecycleOwner.lifecycle.addObserver(LifecycleEventObserver { _, event -> if (event == Lifecycle.Event.ON_DESTROY) { navBackStackEntry.lifecycle.removeObserver(observer) } }) }
Java
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); NavController navController = NavHostFragment.findNavController(this); // After a configuration change or process death, the currentBackStackEntry // points to the dialog destination, so you must use getBackStackEntry() // with the specific ID of your destination to ensure we always // get the right NavBackStackEntry final NavBackStackEntry navBackStackEntry = navController.getBackStackEntry(R.id.your_fragment); // Create our observer and add it to the NavBackStackEntry's lifecycle final LifecycleEventObserver observer = new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event.equals(Lifecycle.Event.ON_RESUME) && navBackStackEntry.getSavedStateHandle().contains("key")) { String result = navBackStackEntry.getSavedStateHandle().get("key"); // Do something with the result } } }; navBackStackEntry.getLifecycle().addObserver(observer); // As addObserver() does not automatically remove the observer, we // call removeObserver() manually when the view lifecycle is destroyed getViewLifecycleOwner().getLifecycle().addObserver(new LifecycleEventObserver() { @Override public void onStateChanged(@NonNull LifecycleOwner source, @NonNull Lifecycle.Event event) { if (event.equals(Lifecycle.Event.ON_DESTROY)) { navBackStackEntry.getLifecycle().removeObserver(observer) } } }); }
مشاركة البيانات المتعلقة بواجهة المستخدم بين الوجهات باستخدام ViewModel
تخزِّن حزمة التنقّل للخلف رمز NavBackStackEntry
ليس فقط لكل وجهة فردية، ولكن أيضًا لكل رسم بياني رئيسي للتنقل
يحتوي على وجهة فردية. ويتيح لك هذا الإجراء استرداد
NavBackStackEntry
تم تحديد نطاقه على رسم بياني للتنقّل. إنّ NavBackStackEntry
على مستوى الرسم البياني للتنقل يوفّر طريقة لإنشاء ViewModel
يتم تحديد نطاقها على رسم بياني للتنقّل، ما يتيح لك مشاركة البيانات المتعلقة بواجهة المستخدم بين وجهات الرسم البياني. تظل أي كائنات ViewModel
تم إنشاؤها بهذه الطريقة متاحة إلى أن
يتم محو NavHost
المرتبطة وViewModelStore
الخاصة بها أو حتى
نبثق الرسم البياني للتنقّل من الحزمة الخلفية.
يوضّح المثال التالي كيفية استرداد ViewModel
تم تحديده في رسم بياني للتنقّل:
Kotlin
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph)
Java
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); MyViewModel viewModel = new ViewModelProvider(backStackEntry).get(MyViewModel.class);
إذا كنت تستخدم الإصدار 2.2.0 أو إصدارًا أقدم، عليك تحديد المصنع الخاص بك لاستخدام الحالة المحفوظة مع ViewModels، كما هو موضح في المثال التالي:
Kotlin
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph) { SavedStateViewModelFactory(requireActivity().application, requireParentFragment()) }
Java
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); ViewModelProvider viewModelProvider = new ViewModelProvider( backStackEntry.getViewModelStore(), new SavedStateViewModelFactory( requireActivity().getApplication(), requireParentFragment())); MyViewModel myViewModel = provider.get(myViewModel.getClass());
لمزيد من المعلومات عن ViewModel
، يُرجى الاطّلاع على
نظرة عامة على ViewModel.
تعديل الرسوم البيانية المضخَّمة للتنقل
ويمكنك تعديل رسم بياني متضخم للتنقل بشكل ديناميكي في وقت التشغيل.
على سبيل المثال، إذا كان لديك
BottomNavigationView
مرتبط بـ NavGraph
، تحدد الوجهة التلقائية
NavGraph
علامة التبويب المحدَّدة عند بدء تشغيل التطبيق. ومع ذلك، قد تحتاج إلى إلغاء هذا السلوك، مثلاً عندما يحدِّد أحد الإعدادات المفضَّلة للمستخدم علامة تبويب مفضَّلة ليتم تحميلها عند بدء تشغيل التطبيق. بدلاً من ذلك، قد يحتاج تطبيقك إلى تغيير علامة تبويب
البدء بناءً على سلوك المستخدم السابق. يمكنك دعم هذه الحالات من خلال تحديد الوجهة التلقائية NavGraph
بشكل ديناميكي.
ضع في الاعتبار NavGraph
هذا:
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph" app:startDestination="@id/home"> <fragment android:id="@+id/home" android:name="com.example.android.navigation.HomeFragment" android:label="fragment_home" tools:layout="@layout/fragment_home" /> <fragment android:id="@+id/location" android:name="com.example.android.navigation.LocationFragment" android:label="fragment_location" tools:layout="@layout/fragment_location" /> <fragment android:id="@+id/shop" android:name="com.example.android.navigation.ShopFragment" android:label="fragment_shop" tools:layout="@layout/fragment_shop" /> <fragment android:id="@+id/settings" android:name="com.example.android.navigation.SettingsFragment" android:label="fragment_settings" tools:layout="@layout/fragment_settings" /> </navigation>
عند تحميل هذا الرسم البياني، تحدّد السمة app:startDestination
أنه سيتم عرض HomeFragment
. لتجاوز وجهة البداية بشكل ديناميكي،
قم بما يلي:
- عليك أولاً تضخيم
NavGraph
يدويًا. - إلغاء وجهة البدء.
- وأخيرًا، أرفق الرسم البياني يدويًا بـ
NavController
.
Kotlin
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController val navGraph = navController.navInflater.inflate(R.navigation.bottom_nav_graph) navGraph.startDestination = R.id.shop navController.graph = navGraph binding.bottomNavView.setupWithNavController(navController)
Java
NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager() .findFragmentById(R.id.nav_host_fragment); NavController navController = navHostFragment.getNavController(); NavGraph navGraph = navController.getNavInflater().inflate(R.navigation.bottom_nav_graph); navGraph.setStartDestination(R.id.shop); navController.setGraph(navGraph); NavigationUI.setupWithNavController(binding.bottomNavView, navController);
الآن عند بدء تشغيل تطبيقك، يتم عرض ShopFragment
بدلاً من HomeFragment
.
عند استخدام الروابط لصفحات معيّنة، تنشئ NavController
حِزمة خلفية تلقائيًا لوجهة الرابط لصفحة معيّنة في التطبيق. إذا انتقل المستخدم إلى رابط الصفحة في التطبيق ثم انتقل للخلف، سيصل إلى وجهة البداية في وقت ما. تجاوز وجهة البداية باستخدام الأسلوب الوارد في المثال السابق يضمن إضافة وجهة البدء الصحيحة إلى المكدس الخلفي الذي تم إنشاؤه.
يُرجى العِلم أنّ هذه التقنية تتيح أيضًا إلغاء الجوانب الأخرى للسمة NavGraph
حسب الحاجة. يجب إجراء جميع التعديلات على الرسم البياني
قبل استدعاء الدالة setGraph()
لضمان استخدام البنية الصحيحة
عند التعامل مع الروابط لمواضع معيّنة واستعادة الحالة والانتقال إلى وجهة
بداية الرسم البياني.