Die Komponente „Navigation“ bietet Möglichkeiten zur programmatischen Erstellung und Interaktion mit bestimmten Navigationselementen.
NavHostFragment erstellen
Sie können
NavHostFragment.create()
um programmatisch eine NavHostFragment
mit einer bestimmten Grafikressource zu erstellen,
wie im folgenden Beispiel gezeigt:
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();
Mit setPrimaryNavigationFragment(finalHost)
kann dein NavHost
das Drücken der Zurück-Taste des Systems abfangen. Sie können dieses Verhalten auch in
deine NavHost
-XML-Datei durch Hinzufügen von app:defaultNavHost="true"
. Wenn Sie eine
benutzerdefiniertes Verhalten der Schaltfläche „Zurück“
und nicht möchte, dass dein NavHost
das Drücken der Zurück-Taste abfängt, kannst du
null
bis setPrimaryNavigationFragment()
.
Mit NavBackStackEntry auf ein Ziel verweisen
Ab Navigation 2.2.0 können Sie einen Verweis auf die
NavBackStackEntry
für jedes Ziel im Navigationsstack erstellen, indem Sie
NavController.getBackStackEntry()
,
und eine Ziel-ID übergeben. Wenn der Back-Stack mehr als eine Instanz enthält
des angegebenen Ziels gibt getBackStackEntry()
die höchste Instanz zurück.
aus dem Stack.
Die zurückgegebene NavBackStackEntry
stellt ein
Lifecycle
,
ViewModelStore
und ein
SavedStateRegistry
auf Zielebene an. Diese Objekte sind für die gesamte Lebensdauer des Ziels im Back-Stack gültig. Wenn das zugehörige Ziel vom
Backstack wird Lifecycle
gelöscht, der Status wird nicht mehr gespeichert und alle ViewModel
-Objekte werden gelöscht.
Diese Attribute liefern Ihnen eine Lifecycle
und einen Speicher für ViewModel
-Objekte und
Kurse, die mit den
gespeicherten Status, egal in welcher
welche Art von Ziel Sie verwenden. Dies ist besonders nützlich, wenn Sie
Zieltypen, denen nicht automatisch ein Lifecycle
zugeordnet ist,
wie etwa benutzerdefinierte Ziele.
So können Sie den Lifecycle
eines NavBackStackEntry
genau wie
würden Sie den Lifecycle
eines Fragments oder einer Aktivität beobachten. Außerdem
NavBackStackEntry
ist ein LifecycleOwner
, was bedeutet, dass Sie ihn verwenden können,
bei der Beobachtung von LiveData
oder anderen
lebenszyklusbezogenen Komponenten, wie in der
folgendes Beispiel:
Kotlin
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
Java
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
Der Lebenszyklusstatus wird automatisch aktualisiert, wenn Sie navigate()
aufrufen.
Lebenszyklusstatus für Ziele, die sich nicht oben im Back-Stack befinden
Wechseln Sie von RESUMED
nach STARTED
, wenn die Ziele immer noch unter einem
FloatingWindow
-Ziel, z. B. ein Dialogziel, oder an STOPPED
sonst.
Ergebnis zum vorherigen Ziel zurückgeben
Ab Navigation 2.3 gewährt NavBackStackEntry
Zugriff auf
SavedStateHandle
Eine SavedStateHandle
ist eine Schlüssel/Wert-Zuordnung, die zum Speichern und Abrufen
Daten. Diese Werte bleiben bis zum Ende des Prozesses erhalten, einschließlich der Konfiguration.
und bleiben über dasselbe Objekt verfügbar. Mit dem vorgegebenen
SavedStateHandle
können Sie auf Daten zwischen Zielen zugreifen und diese übergeben.
Dies ist besonders nützlich, um Daten von einem
nachdem es aus dem Stack entfernt wurde.
Um Daten von Ziel B an Ziel A zurückzugeben,
Ziel A so eingerichtet, dass auf SavedStateHandle
ein Ergebnis erfasst wird.
Rufen Sie dazu den NavBackStackEntry
mit dem
getCurrentBackStackEntry()
API und dann observe
LiveData
bereitgestellt von 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. } }); }
In Ziel B müssen Sie das Ergebnis auf SavedStateHandle
von set
Ziel A mithilfe der getPreviousBackStackEntry()
API.
Kotlin
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Java
navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);
Wenn Sie ein Ergebnis nur einmal verarbeiten möchten, müssen Sie
remove()
auf SavedStateHandle
, um das Ergebnis zu löschen. Wenn Sie die
gibt LiveData
weiterhin das letzte Ergebnis für alle
neue Observer
-Instanzen.
Überlegungen bei der Verwendung von Dialogzielen
Wenn Sie mit dem navigate
zu einem Ziel gelangen, das eine vollständige Ansicht der
NavHost
(z. B. ein <fragment>
-Ziel), das vorherige Ziel
wird beendet, wodurch Rückrufe an die LiveData
verhindert werden.
bereitgestellt von SavedStateHandle
.
Wenn Sie jedoch zu einer
Zieldialogfeld
ist das vorherige Ziel auch auf dem Bildschirm sichtbar und wird daher
STARTED
, obwohl es nicht das aktuelle Ziel ist. Das bedeutet, dass Aufrufe an
getCurrentBackStackEntry()
aus Lebenszyklusmethoden wie
onViewCreated()
gibt die NavBackStackEntry
des Dialogfeldziels zurück
nach einer Konfigurationsänderung oder nach dem Ableben
des Prozesses und der Wiederherstellung (da der Dialog
wird über dem anderen Ziel wiederhergestellt. Daher sollten Sie
getBackStackEntry()
durch die ID Ihres Ziels, damit Sie immer die richtige
NavBackStackEntry
.
Das bedeutet auch, dass jede Observer
, die Sie für das Ergebnis LiveData
festlegen,
wird ausgelöst, auch wenn die Ziele des Dialogfelds noch auf dem Bildschirm zu sehen sind. Wenn Sie
das Ergebnis nur überprüfen möchten, wenn das Ziel des Dialogfelds geschlossen und der
das zugrunde liegende Ziel zum aktuellen Ziel wird,
Lifecycle
, die mit NavBackStackEntry
verknüpft sind, und das Ergebnis abrufen
wenn es RESUMED
wird.
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) } } }); }
UI-bezogene Daten zwischen Zielen mit ViewModel teilen
Im Back Stack der Navigation
NavBackStackEntry
nicht nur für jedes einzelne Ziel, sondern auch für jede übergeordnete Navigation
die das individuelle Ziel enthält. So können Sie eine
NavBackStackEntry
, der einem Navigationsdiagramm zugeordnet ist. Eine Navigation
diagrammbezogenen NavBackStackEntry
können Sie eine ViewModel
erstellen, die
die sich auf ein Navigationsdiagramm beziehen, sodass Sie UI-bezogene Daten zwischen den
die Ziele der Grafik. Alle auf diese Weise erstellten ViewModel
-Objekte sind gültig bis zum
die verknüpfte NavHost
und die zugehörigen ViewModelStore
gelöscht werden oder bis die
das Navigationsdiagramm aus dem Back Stack entnommen wird.
Das folgende Beispiel zeigt, wie Sie ein ViewModel
abrufen, das einer
Navigationsdiagramm:
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);
Wenn Sie Navigation 2.2.0 oder älter verwenden, müssen Sie Ihre eigene Factory- Gespeicherter Status mit ViewModels Dies wird im folgenden Beispiel gezeigt:
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());
Weitere Informationen zu ViewModel
finden Sie unter
ViewModel – Übersicht.
Überhöhte Navigationsdiagramme ändern
Sie können ein aufgeblähtes Navigationsdiagramm zur Laufzeit dynamisch ändern.
Wenn Sie zum Beispiel eine
BottomNavigationView
die an ein NavGraph
gebunden ist, das Standardziel der
NavGraph
gibt den ausgewählten Tab beim Start der App vor. Sie können jedoch
dieses Verhalten außer Kraft setzen, z. B. wenn in einer Nutzereinstellung
ein bevorzugter Tab, der beim Start der App geladen wird. Alternativ könnte Ihre App
den Start-Tab basierend auf dem bisherigen Nutzerverhalten ändern. Sie können
unterstützen diese Fälle, indem das Standardziel
NavGraph
.
Sehen Sie sich diese NavGraph
an:
<?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>
Wenn diese Grafik geladen wird, gibt das Attribut app:startDestination
Folgendes an:
dass HomeFragment
angezeigt werden soll. Um das Startziel zu überschreiben
dynamisch:
- Drehe zuerst
NavGraph
manuell auf. - Startziel überschreiben.
- Hängen Sie das Diagramm abschließend manuell an
NavController
an.
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);
Wenn deine App jetzt gestartet wird, wird ShopFragment
statt HomeFragment
angezeigt.
Bei der Verwendung von Deeplinks erstellt NavController
einen Back-Stack
automatisch für das Deeplink-Ziel. Wenn der Nutzer
zum Deeplink navigiert und dann rückwärts geht, erreicht die
Startziel zu erreichen. Das Startziel wird mithilfe der
im vorherigen Beispiel dafür sorgen, dass der richtige Start
wird dem erstellten Back-Stack hinzugefügt.
Beachten Sie, dass mit dieser Technik auch andere Aspekte
NavGraph
nach Bedarf. Alle Änderungen an der Grafik müssen vorgenommen werden
vor dem Aufruf von setGraph()
, um sicherzustellen,
wird beim Verarbeiten von Deeplinks, beim Wiederherstellen des Zustands und beim Wechsel an den Anfang verwendet
Ziel des Diagramms.