Programmatisch mit der Navigationskomponente interagieren

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().

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.
        }
    });
}
<ph type="x-smartling-placeholder">

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)
            }
        }
    });
}

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:

  1. Drehe zuerst NavGraph manuell auf.
  2. Startziel überschreiben.
  3. 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.