Komponent Nawigacja umożliwia programowe tworzenie i interakcję z określonymi elementami nawigacyjnymi.
Tworzenie fragmentu NavHostFragment
Za pomocą
NavHostFragment.create()
aby automatycznie utworzyć NavHostFragment
z określonym zasobem grafu,
jak w tym przykładzie:
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();
Pamiętaj, że setPrimaryNavigationFragment(finalHost)
umożliwia: NavHost
przechwytywania naciśnięć przycisku Wstecz w systemie. Możesz również zastosować to działanie w:
NavHost
, dodając app:defaultNavHost="true"
. Jeśli implementujesz
niestandardowe zachowanie przycisku Wstecz
i nie chcesz, aby urządzenie NavHost
przechwytowało naciśnięcia przycisku Wstecz,
null
do setPrimaryNavigationFragment()
.
Odwoływanie się do miejsca docelowego za pomocą NavBackStackEntry
Począwszy od Nawigacji w wersji 2.2.0, możesz mieć dostęp do
NavBackStackEntry
dla dowolnego miejsca docelowego ze stosu nawigacji, wywołując
NavController.getBackStackEntry()
,
i przekazujemy mu identyfikator miejsca docelowego. Jeśli stos tylny zawiera więcej niż jedną instancję
wskazanego miejsca docelowego, getBackStackEntry()
zwraca instancję najwyższego poziomu
z grupy.
Zwrócona wartość NavBackStackEntry
zapewnia
Lifecycle
ViewModelStore
oraz
SavedStateRegistry
na poziomie miejsca docelowego. Te obiekty są ważne przez cały okres istnienia miejsca docelowego na stosie wstecznym. Gdy powiązane miejsce docelowe zostanie wydzielone z
stos wsteczny, obiekt Lifecycle
zostaje zniszczony, stan nie jest już zapisywany, a wszystkie obiekty ViewModel
zostały wyczyszczone.
Te właściwości zapewniają Lifecycle
oraz magazyn obiektów ViewModel
i
zajęcia, z którymi
stan zapisany niezależnie
rodzaj miejsca docelowego, z którego korzystasz. Jest to szczególnie przydatne podczas pracy z
typy miejsc docelowych, które nie mają automatycznie powiązanego atrybutu Lifecycle
,
takich jak niestandardowe miejsca docelowe.
Na przykład możesz obserwować Lifecycle
elementu NavBackStackEntry
tak samo
można obserwować Lifecycle
fragmentu lub aktywności. Ponadto
NavBackStackEntry
to LifecycleOwner
, co oznacza, że możesz go używać, gdy
obserwując wartość LiveData
lub inne komponenty uwzględniające cykl życia, co pokazuje
następujący przykład:
Kotlin
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
Java
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
Stan cyklu życia jest automatycznie aktualizowany za każdym razem, gdy wywołujesz funkcję navigate()
.
Stany cyklu życia dla miejsc docelowych, które nie znajdują się na górze stosu tylnego
przejdź z RESUMED
do STARTED
, jeśli miejsca docelowe są nadal widoczne w
miejsce docelowe FloatingWindow
, takie jak miejsce docelowe w oknie dialogowym, lub do STOPPED
w przeciwnym razie.
Zwracanie wyniku do poprzedniego miejsca docelowego
W Nawigacji w wersji 2.3 i nowszych NavBackStackEntry
umożliwia dostęp do funkcji
SavedStateHandle
SavedStateHandle
to mapa klucz-wartość, której można używać do przechowywania i pobierania danych
i skalowalnych danych. Te wartości są utrwalone na etapie zakończenia procesu, w tym konfiguracji
zmian i pozostają dostępne w ramach tego samego obiektu. Korzystając z parametru
SavedStateHandle
, możesz uzyskiwać dostęp do danych i przekazywać je między miejscami docelowymi.
Jest to szczególnie przydatne jako mechanizm odzyskiwania danych z
miejsce docelowe po jego wyrzuceniu ze stosu.
Aby przekazać dane z miejsca docelowego A z powrotem do miejsca docelowego B, najpierw
skonfigurować miejsce docelowe A, aby nasłuchiwać wyniku na jego urządzeniu SavedStateHandle
.
Aby to zrobić, pobierz NavBackStackEntry
za pomocą polecenia
getCurrentBackStackEntry()
API, a następnie observe
LiveData
źródło: 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. } }); }
![Miejsce docelowe B może pobrać za pomocą metody getPreviousBackStackEntry()
wartość NavBackStackEntry w przypadku poprzedniego miejsca docelowego A](https://developer.android.google.cn/static/images/guide/navigation/nav-for-result.png?authuser=19&hl=pl)
W miejscu docelowym B musisz set
wynik w SavedStateHandle
Miejsce docelowe A za pomocą interfejsu getPreviousBackStackEntry()
API.
Kotlin
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Java
navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);
Jeśli chcesz przetworzyć wynik tylko raz, musisz wywołać
remove()
na urządzeniu SavedStateHandle
, aby wyczyścić wynik. Jeśli nie usuniesz
wynik, LiveData
będzie nadal zwracać ostatni wynik wszystkim
nowych instancji (Observer
).
O czym warto pamiętać podczas korzystania z miejsc docelowych okien
Gdy navigate
dotrze do miejsca docelowego z pełnym widokiem
NavHost
(np. <fragment>
) – poprzedni cel podróży
został zatrzymany cykl życia, co zapobiega wywoływaniu przez nie wywołań zwrotnych do LiveData
źródło: SavedStateHandle
.
Po przejściu do
dialog destination,
poprzedni cel podróży jest również widoczny na ekranie i dlatego jest także
STARTED
, mimo że nie jest to obecny cel podróży. Oznacza to, że wywołania
getCurrentBackStackEntry()
z metod cyklu życia, takich jak
onViewCreated()
zwraca wartość NavBackStackEntry
adresu docelowego okna.
po zmianie konfiguracji albo procesie śmierci lub rekreacji (od
nad innym miejscem docelowym). Dlatego
getBackStackEntry()
za pomocą identyfikatora miejsca docelowego, aby zawsze używać prawidłowego
NavBackStackEntry
Oznacza to również, że każdy element Observer
ustawiony w wyniku LiveData
zostanie
jest wyzwalana, nawet gdy na ekranie wciąż znajdują się miejsca docelowe okien. Jeśli
chcesz sprawdzać wynik tylko wtedy, gdy miejsce docelowe w oknie jest zamknięte, a przycisk
bazowe miejsce docelowe stanie się bieżącym miejscem docelowym, możesz obserwować
Powiązanie Lifecycle
z obiektem NavBackStackEntry
i pobieranie wyniku
tylko wtedy, gdy zmieni się on na 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) } } }); }
Udostępniaj dane związane z interfejsem użytkownika między miejscami docelowymi za pomocą ViewModel
Stos nawigacji wstecz zawiera
NavBackStackEntry
nie tylko w przypadku poszczególnych miejsc docelowych, ale również dla poszczególnych elementów nawigacji
który zawiera konkretne miejsce docelowe. Dzięki temu możesz pobrać
NavBackStackEntry
, którego zakres jest ograniczony do wykresu nawigacyjnego. Nawigacja
funkcji NavBackStackEntry
o zakresie na wykresie pozwala utworzyć obiekt ViewModel
, który
ograniczone do wykresu nawigacyjnego, co pozwala udostępniać dane dotyczące interfejsu
miejsc docelowych wykresu. Wszystkie utworzone w ten sposób obiekty ViewModel
są aktywne do
powiązane pole NavHost
i jego ViewModelStore
są wyczyszczone lub do momentu
jest wyskakujący ze stosu tylnego.
Poniższy przykład pokazuje, jak pobrać obiekt ViewModel
ograniczony do zakresu
wykres nawigacyjny:
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);
Jeśli korzystasz z Nawigacji w wersji 2.2.0 lub starszej, musisz podać własną do użycia w wersji fabrycznej Saved State with ViewModels (Zapisany stan za pomocą modeli widoków), jak w tym przykładzie:
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());
Więcej informacji o ViewModel
:
Przegląd modelu widoku.
Modyfikowanie powiększonych wykresów nawigacyjnych
Wykres nawigacyjny możesz modyfikować dynamicznie w czasie działania.
Na przykład, jeśli masz
BottomNavigationView
powiązany z miejscem docelowym NavGraph
, domyślnym miejscem docelowym
NavGraph
dyktuje wybraną kartę podczas uruchamiania aplikacji. Możesz jednak
muszą zastąpić to zachowanie, na przykład gdy preferencje użytkownika określają
preferowanej karty, która ma być ładowana po uruchomieniu aplikacji. Aplikacja może też
zmienić kartę początkową na podstawie wcześniejszych działań użytkownika. Dostępne opcje
obsługuje takie przypadki, dynamicznie określając domyślne miejsce docelowe
NavGraph
.
Weź pod uwagę 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>
Gdy ten wykres zostanie wczytany, atrybut app:startDestination
określa,
że HomeFragment
ma być wyświetlany. Zastąpienie miejsca docelowego początkowego
wykonaj te czynności:
- Najpierw ręcznie rozszerzaj
NavGraph
. - Zastąp miejsce docelowe początkowego.
- Na koniec ręcznie dołącz wykres do tabeli
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);
Od teraz po uruchomieniu aplikacji zamiast HomeFragment
pojawi się ShopFragment
.
Gdy używasz precyzyjnych linków, NavController
tworzy stos wsteczny
dla miejsca docelowego precyzyjnego linku. Jeśli użytkownik
przechodzi do precyzyjnego linku, a następnie przechodzi wstecz, dociera do
miejsca docelowego. Zastąpienie miejsca docelowego początkowego za pomocą funkcji
w poprzednim przykładzie zapewnia, że właściwy początek
miejsce docelowe jest dodawane do utworzonego stosu wstecznego.
Ta technika pozwala także zastąpić inne aspekty
NavGraph
zgodnie z wymaganiami. Należy wprowadzić wszystkie zmiany w wykresie
przed wywołaniem funkcji setGraph()
, aby zapewnić prawidłową strukturę
jest używany do obsługi precyzyjnych linków, przywracania stanu i przechodzenia na początek
miejsce docelowe wykresu.