يوفر مكوِّن التنقل طرقًا للإنشاء والتفاعل بطريقة آلية مع عناصر تنقل معينة.
إنشاء 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
نظام اعتراض الضغط على زر الرجوع. يمكنك أيضًا تنفيذ هذا السلوك في
ملف NavHost
XML عن طريق إضافة 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
، يمكنك الوصول إلى البيانات وتمريرها بين الوجهات.
وهذا مفيد بشكل خاص كآلية لاسترداد البيانات من
الوجهة بعد خروجه من المكدس.
لتمرير البيانات مرة أخرى إلى الوجهة "أ" من الوجهة "ب"، عليك أولاً
إعداد الوجهة A للاستماع إلى نتيجة على SavedStateHandle
.
لإجراء ذلك، يمكنك استرداد NavBackStackEntry
باستخدام
getCurrentBackStackEntry()
API ثم 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()
للتأكد من أن البنية الصحيحة
يُستخدم عند التعامل مع الروابط لمواضع معيّنة واستعادة الحالة والانتقال إلى نقطة البداية
وجهة الرسم البياني.