Komponen Navigation menyediakan cara untuk membuat dan berinteraksi secara terprogram dengan elemen navigasi tertentu.
Membuat NavHostFragment
Anda dapat menggunakan
NavHostFragment.create()
untuk membuat NavHostFragment
secara terprogram dengan aset grafik tertentu,
seperti yang ditunjukkan dalam contoh di bawah ini:
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();
Perhatikan bahwa setPrimaryNavigationFragment(finalHost)
memungkinkan NavHost
menangkap penekanan tombol Kembali sistem. Anda juga dapat menerapkan perilaku ini dalam
XML NavHost
dengan menambahkan app:defaultNavHost="true"
. Jika Anda menerapkan
perilaku tombol Kembali kustom
dan tidak ingin NavHost
menangkap penekanan tombol Kembali, Anda dapat meneruskan
null
ke setPrimaryNavigationFragment()
.
Mereferensikan tujuan menggunakan NavBackStackEntry
Dimulai dengan Navigation 2.2.0, Anda dapat memperoleh referensi ke
NavBackStackEntry
untuk setiap tujuan di stack navigasi dengan memanggil
NavController.getBackStackEntry()
,
sambil meneruskan ID tujuan ke dalamnya. Jika data sebelumnya berisi lebih dari satu instance dari tujuan yang ditentukan, getBackStackEntry()
menampilkan instance paling atas dari stack.
NavBackStackEntry
yang ditampilkan menyediakan
Lifecycle
, ViewModelStore
, dan SavedStateRegistry
di tingkat tujuan. Objek ini berlaku selama tujuan masih aktif di entri data sebelumnya. Jika tujuan terkait dikeluarkan dari entri data sebelumnya, Lifecycle
dihancurkan, status tidak akan disimpan lagi, dan objek ViewModel
akan dihapus.
Properti ini memberi Anda Lifecycle
dan penyimpanan untuk objek dan class ViewModel
yang berfungsi dengan status tersimpan, apa pun jenis tujuan yang Anda gunakan. Hal ini berguna terutama saat bekerja dengan jenis tujuan yang tidak otomatis memiliki Lifecycle
terkait, seperti tujuan khusus.
Misalnya, Anda dapat mengamati Lifecycle
dari NavBackStackEntry
sama seperti Anda mengamati Lifecycle
fragmen atau aktivitas. Selain itu, NavBackStackEntry
adalah LifecycleOwner
, yang berarti Anda dapat menggunakannya saat mengamati LiveData
atau dengan komponen berbasis siklus proses, seperti yang ditunjukkan dalam contoh berikut:
Kotlin
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
Java
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
Status siklus proses otomatis diupdate setiap kali Anda memanggil navigate()
.
Status siklus proses untuk tujuan yang tidak berada di bagian atas data sebelumnya
berpindah dari RESUMED
ke STARTED
jika tujuan masih terlihat di tujuan
FloatingWindow
, seperti tujuan dialog, atau ke STOPPED
jika tidak.
Menampilkan hasil ke Tujuan sebelumnya
Dalam Navigation 2.3 dan yang lebih tinggi, NavBackStackEntry
memberikan akses ke
SavedStateHandle
.
SavedStateHandle
adalah peta nilai kunci yang dapat digunakan untuk menyimpan dan mengambil
data. Nilai-nilai ini akan dipertahankan setelah proses dihentikan, termasuk perubahan konfigurasi, dan akan tetap tersedia melalui objek yang sama. Dengan menggunakan SavedStateHandle
yang diberikan,
Anda dapat mengakses dan meneruskan data antar-tujuan.
Hal ini sangat berguna sebagai mekanisme untuk mengembalikan data dari tujuan setelah data dikeluarkan dari stack.
Untuk meneruskan data kembali dari Tujuan B ke Tujuan A, tetapkan Tujuan A untuk memproses hasil di SavedStateHandle
-nya.
Untuk melakukannya, ambil NavBackStackEntry
dengan menggunakan API getCurrentBackStackEntry()
kemudian observe
LiveData
yang disediakan oleh 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. } }); }
Di Tujuan B, Anda harus set
hasil di SavedStateHandle
Tujuan A menggunakan API getPreviousBackStackEntry()
.
Kotlin
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Java
navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);
Jika hanya ingin menangani hasil satu kali, Anda harus memanggil remove()
di SavedStateHandle
untuk menghapus hasil. Jika hasil tidak dihapus, LiveData
akan terus menampilkan hasil terakhir ke instance Observer
baru.
Pertimbangan saat menggunakan tujuan dialog
Saat Anda melakukan navigate
ke tujuan yang mengambil tampilan lengkap NavHost
(seperti tujuan <fragment>
), tujuan sebelumnya akan membuat siklus prosesnya terhenti, sehingga mencegah callback ke LiveData
yang diberikan oleh SavedStateHandle
.
Namun, saat menavigasi ke tujuan dialog, tujuan sebelumnya juga akan terlihat di layar dan juga STARTED
meskipun bukan tujuan saat ini. Ini berarti bahwa panggilan ke getCurrentBackStackEntry()
dari dalam metode siklus proses seperti onViewCreated()
akan mengembalikan NavBackStackEntry
tujuan dialog setelah perubahan konfigurasi atau proses dihentikan dan dibuat ulang (mengingat dialog dikembalikan di atas tujuan lainnya). Oleh karena itu, Anda harus menggunakan getBackStackEntry()
dengan ID tujuan untuk memastikan bahwa Anda selalu menggunakan NavBackStackEntry
yang benar.
Ini juga berarti bahwa Observer
mana pun yang Anda tetapkan pada hasil LiveData
akan terpicu meskipun tujuan dialog masih ada di layar. Jika hanya ingin memeriksa hasilnya saat tujuan dialog ditutup dan tujuan dasar menjadi tujuan saat ini, Anda dapat mengamati Lifecycle
yang terkait dengan NavBackStackEntry
dan mengambil hasilnya setelah menjadi 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) } } }); }
Membagikan data terkait UI antar-tujuan dengan ViewModel
Entri data sebelumnya dari Navigation menyimpan NavBackStackEntry
tidak hanya untuk setiap tujuan individu, tetapi juga untuk setiap grafik navigasi induk yang berisi tujuan individual. Hal ini memungkinkan Anda mengambil NavBackStackEntry
yang dicakup dalam grafik navigasi. NavBackStackEntry
cakupan grafik navigasi menyediakan cara untuk membuat ViewModel
yang dicakupkan ke grafik navigasi, sehingga Anda dapat berbagi data terkait UI antar-tujuan grafik. Setiap objek ViewModel
yang dibuat dengan cara ini akan aktif sampai
NavHost
yang terkait dan ViewModelStore
-nya dibersihkan atau sampai
grafik navigasi muncul dari entri data sebelumnya.
Contoh berikut menunjukkan cara mengambil ViewModel
yang dicakup dalam
grafik navigasi:
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);
Jika menggunakan Navigation 2.2.0 atau versi yang lebih lama, Anda harus menyediakan factory sendiri untuk menggunakan Status Tersimpan dengan ViewModels, seperti yang ditunjukkan dalam contoh berikut:
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());
Untuk mengetahui informasi selengkapnya tentang ViewModel
, lihat
Ringkasan ViewModel.
Mengubah grafik navigasi yang di-inflate
Anda dapat memodifikasi grafik navigasi yang di-inflate secara dinamis saat runtime.
Misalnya, jika Anda memiliki
BottomNavigationView
yang terikat dengan NavGraph
, tujuan default
NavGraph
akan menentukan tab yang dipilih saat aplikasi dimulai. Namun, Anda mungkin
perlu mengganti perilaku ini, seperti saat preferensi pengguna menentukan
tab pilihan untuk dimuat saat aplikasi dimulai. Atau, aplikasi Anda mungkin
perlu mengubah tab awal berdasarkan perilaku pengguna pada masa lalu. Anda dapat
mendukung kasus ini dengan menentukan tujuan default
NavGraph
secara dinamis.
Pertimbangkan NavGraph
ini:
<?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>
Saat grafik ini dimuat, atribut app:startDestination
menentukan
bahwa HomeFragment
akan ditampilkan. Untuk mengganti tujuan awal
secara dinamis, lakukan hal berikut:
- Pertama, inflate
NavGraph
secara manual. - Ganti tujuan awal.
- Terakhir, lampirkan grafik secara manual ke
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);
Sekarang saat aplikasi Anda dimulai, ShopFragment
akan ditampilkan, bukan HomeFragment
.
Saat menggunakan deep link, NavController
akan membuat data sebelumnya
secara otomatis untuk tujuan deep link. Jika pengguna
membuka deep link, lalu menavigasi mundur, mereka akan tetap mencapai
tujuan awal pada akhirnya. Mengganti tujuan awal menggunakan
teknik dalam contoh sebelumnya memastikan bahwa tujuan awal
yang benar telah ditambahkan ke data sebelumnya yang telah dibuat.
Perhatikan bahwa teknik ini juga memungkinkan penggantian aspek lain dari
NavGraph
sebagaimana diperlukan. Semua modifikasi pada grafik harus dilakukan
sebelum panggilan ke setGraph()
untuk memastikan bahwa struktur yang benar
digunakan saat menangani deep link, memulihkan status, dan berpindah ke tujuan
awal grafik Anda.