مؤلفه 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
شما اجازه می دهد تا دکمه Back را فشار دهد. همچنین میتوانید با افزودن app:defaultNavHost="true"
این رفتار را در NavHost
XML خود پیادهسازی کنید. اگر رفتار دکمه بازگشت سفارشی را پیادهسازی میکنید و نمیخواهید NavHost
شما دکمه Back را رهگیری کند، میتوانید 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 2.3 و بالاتر، NavBackStackEntry
به SavedStateHandle
دسترسی می دهد. SavedStateHandle
یک نقشه کلید-مقدار است که می تواند برای ذخیره و بازیابی داده ها استفاده شود. این مقادیر از طریق مرگ فرآیند، از جمله تغییرات پیکربندی، باقی می مانند و از طریق همان شی در دسترس باقی می مانند. با استفاده از SavedStateHandle
داده شده، می توانید به داده ها بین مقصد دسترسی داشته باشید و آن را ارسال کنید. این به ویژه به عنوان مکانیزمی برای بازگرداندن اطلاعات از یک مقصد پس از بیرون آمدن از پشته مفید است.
برای بازگرداندن داده ها به مقصد A از مقصد B، ابتدا مقصد A را تنظیم کنید تا به نتیجه در SavedStateHandle
آن گوش دهد. برای انجام این کار، NavBackStackEntry
با استفاده از getCurrentBackStackEntry()
API بازیابی کنید و سپس LiveData
ارائه شده توسط SavedStateHandle
observe
.
کاتلین
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. } }); }
در Destination B، باید نتیجه را در SavedStateHandle
مقصد A با استفاده از getPreviousBackStackEntry()
API 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 به اشتراک بگذارید
پشته Navigation back یک 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 2.2.0 یا نسخه قبلی استفاده می کنید، باید کارخانه خود را برای استفاده از حالت ذخیره شده با 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 Overview مراجعه کنید.
اصلاح نمودارهای ناوبری متورم
شما می توانید یک نمودار ناوبری متورم را به صورت پویا در زمان اجرا تغییر دهید.
به عنوان مثال، اگر 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()
انجام شود تا اطمینان حاصل شود که ساختار صحیح هنگام مدیریت پیوندهای عمیق، بازیابی حالت و حرکت به مقصد شروع گراف شما استفاده می شود.