مؤلفه Navigation روشهایی برای ایجاد و تعامل برنامهنویسیشده با برخی عناصر پیمایش ارائه میدهد.
ایجاد NavHostFragment
میتوانید از
NavHostFragment.create()
برای ایجاد برنامهنویسیشده NavHostFragment با منبع نمودار خاصی استفاده کنید،
همانطور که در مثال زیر نشان داده شده است:
کاتلین
val finalHost = NavHostFragment.create(R.navigation.example_graph) supportFragmentManager.beginTransaction() .replace(R.id.nav_host, finalHost) .setPrimaryNavigationFragment(finalHost) // equivalent to app:defaultNavHost="true" .commit()
جاوا
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
با شروع Navigation 2.2.0، میتوانید با فراخوانی
NavController.getBackStackEntry()
و ارسال شناسه مقصد به آن، مرجعی به
NavBackStackEntry
برای هر مقصدی در پشته ناوبری دریافت کنید. اگر پشته قبلیها حاوی بیشاز یک نمونه از مقصد مشخصشده باشد، getBackStackEntry() بالاترین نمونه را از پشته برمیگرداند.
NavBackStackEntry برگرداندهشده
Lifecycle،
ViewModelStore، و
SavedStateRegistry
در سطح مقصد ارائه میدهد. این اشیاء برای طول عمر مقصد در پشته قبلیها معتبر هستند. وقتی مقصد مرتبط از پشته قبلیها خارج میشود، Lifecycle ازبین میرود، وضعیت دیگر ذخیره نمیشود، و همه اشیای ViewModel پاک میشوند.
این ویژگیها به شما یک Lifecycle و یک فروشگاه برای اشیاء و کلاسهای ViewModel میدهند که با وضعیت ذخیرهشده کار میکنند، صرفنظر از اینکه از چه نوع مقصدی استفاده میکنید. این ویژگی بهویژه هنگام کار با انواع مقصدهایی که بهطور خودکار Lifecycle مرتبط ندارند، مانند مقصدهای سفارشی، مفید است.
برای مثال، میتوانید Lifecycle یک NavBackStackEntry را همانطور که Lifecycle یک قطعه یا فعالیت را مشاهده میکنید، مشاهده کنید. علاوهبراین،
NavBackStackEntry یک LifecycleOwner است، یعنی میتوانید هنگام
مشاهده LiveData یا با دیگر مؤلفههای آگاه از چرخه حیات از آن استفاده کنید، همانطور که در
مثال زیر نشان داده شده است:
کاتلین
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
جاوا
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
هرزمان navigate() را فراخوانی کنید، وضعیت چرخه حیات بهطور خودکار بهروز میشود.
وضعیتهای چرخه حیات برای مقصدهایی که در بالای پشته قبلیها نیستند
از RESUMED به STARTED منتقل میشوند اگر مقصدها هنوز در زیر یک
مقصد FloatingWindow، مانند مقصد گفتگو، قابلمشاهده باشند، یا درغیراینصورت به STOPPED منتقل میشوند.
درحال برگرداندن نتیجه به «مقصد» قبلی
در Navigation نسخه ۲.۳ و بالاتر، NavBackStackEntry دسترسی به
SavedStateHandle را فراهم میکند.
SavedStateHandle یک نگاشت مقدار کلیدی است که میتوان از آن برای ذخیره و بازیابی
دادهها استفاده کرد. این مقادیر درطول مرگ پردازش، ازجمله تغییرات پیکربندی، باقی میمانند و ازطریق همان شیء دردسترس میمانند. بااستفاده از
SavedStateHandle ارائهشده، میتوانید به دادهها دسترسی داشته باشید و آنها را بین مقصدها منتقل کنید.
این بهویژه بهعنوان مکانیزمی برای بازگرداندن دادهها از مقصد پساز خارج شدن از پشته مفید است.
برای برگرداندن دادهها از «مقصد ب» به «مقصد الف»، ابتدا
«مقصد الف» را طوری تنظیم کنید که منتظر نتیجه در SavedStateHandle خود باشد.
برای انجام این کار، NavBackStackEntry را بااستفاده از
getCurrentBackStackEntry() API بازیابی کنید و سپس observe LiveData
ارائهشده توسط SavedStateHandle.
کاتلین
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. } }
جاوا
@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. } }); }
در «مقصد ب»، باید نتیجه را در SavedStateHandle «مقصد الف» بااستفاده از میانای برنامهسازی کاربردی getPreviousBackStackEntry() set کنید.
کاتلین
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
جاوا
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 تبدیل میشود.
کاتلین
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) } }) }
جاوا
@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 را که در محدوده نمودار پیمایش است نشان میدهد:
کاتلین
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph)
جاوا
NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); MyViewModel viewModel = new ViewModelProvider(backStackEntry).get(MyViewModel.class);
اگر از Navigation نسخه ۲.۲.۰ یا قدیمیتر استفاده میکنید، باید کارخانه خودتان را برای استفاده از Saved State با ViewModels ارائه دهید، همانطور که در مثال زیر نشان داده شده است:
کاتلین
val viewModel: MyViewModel by navGraphViewModels(R.id.my_graph) { SavedStateViewModelFactory(requireActivity().application, requireParentFragment()) }
جاوا
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پیوست کنید.
کاتلین
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)
جاوا
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() انجام شود تا اطمینان حاصل شود که ساختار صحیح
هنگام مدیریت پیوندهای عمیق، بازیابی وضعیت، و رفتن به مقصد شروع
نمودار شما استفاده میشود.