Il componente Navigazione offre vari modi per creare e interagire in modo programmatico con alcuni elementi di navigazione.
Crea un NavHostFragment
Puoi utilizzare
NavHostFragment.create()
per creare in modo programmatico un elemento NavHostFragment
con una specifica risorsa di grafico,
come mostrato nell'esempio seguente:
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();
Tieni presente che setPrimaryNavigationFragment(finalHost)
consente a NavHost
intercetta le pressioni del pulsante Indietro di sistema. Puoi implementare questo comportamento anche
il tuo XML NavHost
aggiungendo app:defaultNavHost="true"
. Se implementi
comportamento del pulsante Indietro personalizzato
e non vuoi che NavHost
intercetta la pressione del pulsante Indietro, puoi passare
Da null
a setPrimaryNavigationFragment()
.
Riferimento a una destinazione utilizzando NavBackStackEntry
A partire da Navigazione 2.2.0, puoi ottenere un riferimento
NavBackStackEntry
per qualsiasi destinazione nello stack di navigazione,
NavController.getBackStackEntry()
,
passando un ID destinazione. Se lo stack posteriore contiene più di un'istanza
della destinazione specificata, getBackStackEntry()
restituisce l'istanza di livello più alto.
dall'elenco.
Il valore NavBackStackEntry
restituito fornisce un'istanza
Lifecycle
, un
ViewModelStore
e un
SavedStateRegistry
a livello di destinazione. Questi oggetti sono validi per tutta la durata della destinazione nel back stack. Quando la destinazione associata viene separata dalla
back stack, l'oggetto Lifecycle
viene eliminato, lo stato non viene più salvato e gli oggetti ViewModel
vengono cancellati.
Queste proprietà offrono un Lifecycle
e un archivio per ViewModel
oggetti e
che funzionano con
stato salvato a prescindere
dal tipo di destinazione che utilizzi. Ciò è particolarmente utile quando si lavora
tipi di destinazione a cui non è associato automaticamente un valore Lifecycle
,
ad esempio le destinazioni personalizzate.
Ad esempio, puoi osservare il Lifecycle
di un NavBackStackEntry
proprio come
osserveresti il valore Lifecycle
di un frammento o un'attività. Inoltre,
NavBackStackEntry
è un LifecycleOwner
, il che significa che puoi utilizzarlo quando
osservando LiveData
o con altri componenti sensibili al ciclo di vita, come mostrato
nell'esempio seguente:
Kotlin
myViewModel.liveData.observe(backStackEntry, Observer { myData -> // react to live data update })
Java
myViewModel.getLiveData().observe(backStackEntry, myData -> { // react to live data update });
Lo stato del ciclo di vita si aggiorna automaticamente ogni volta che chiami navigate()
.
Stati del ciclo di vita per le destinazioni che non si trovano in cima al back stack
sposta da RESUMED
a STARTED
se le destinazioni sono ancora visibili sotto un
Destinazione FloatingWindow
, ad esempio una destinazione della finestra di dialogo, o a STOPPED
negli altri casi.
Restituzione di un risultato alla destinazione precedente
Nella Navigazione 2.3 e successive, NavBackStackEntry
consente l'accesso a un
SavedStateHandle
Un SavedStateHandle
è una mappa chiave-valore che può essere utilizzata per archiviare e recuperare
e i dati di Google Cloud. Questi valori persistono anche attraverso la morte del processo, inclusa la configurazione
modifiche e rimangono disponibili tramite lo stesso oggetto. Utilizzando il modello
SavedStateHandle
, puoi accedere ai dati e trasmetterli tra le destinazioni.
Ciò è particolarmente utile come meccanismo per recuperare i dati da un
dopo che è stata rimossa dalla pila.
Per ritrasmettere i dati alla Destinazione A dalla Destinazione B, devi prima
configura la destinazione A per ascoltare un risultato sul relativo SavedStateHandle
.
A questo scopo, recupera NavBackStackEntry
utilizzando il metodo
API getCurrentBackStackEntry()
e quindi observe
il LiveData
fornita da 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. } }); }
Nella Destinazione B, devi set
il risultato nella SavedStateHandle
di
Destinazione A mediante l'API getPreviousBackStackEntry()
.
Kotlin
navController.previousBackStackEntry?.savedStateHandle?.set("key", result)
Java
navController.getPreviousBackStackEntry().getSavedStateHandle().set("key", result);
Se vuoi gestire un risultato solo una volta, devi richiamare
remove()
su SavedStateHandle
per cancellare il risultato. Se non rimuovi
risultato, LiveData
continuerà a restituire l'ultimo risultato a qualsiasi
nuove istanze Observer
.
Considerazioni sull'utilizzo delle destinazioni delle finestre di dialogo
Quando navigate
per raggiungere una destinazione che offre la visualizzazione completa del
NavHost
(ad es. una destinazione <fragment>
), la destinazione precedente
il ciclo di vita è stato interrotto, impedendo qualsiasi callback al LiveData
fornita da SavedStateHandle
.
Tuttavia, quando accedi a
destinazione finestra di dialogo,
la destinazione precedente è visibile sullo schermo ed è quindi anche
STARTED
pur non essendo la destinazione corrente. Ciò significa che le chiamate
getCurrentBackStackEntry()
da metodi del ciclo di vita come
onViewCreated()
restituirà il NavBackStackEntry
della destinazione della finestra di dialogo
dopo una modifica della configurazione o un processo di decesso e ricreazione (dalla finestra di dialogo
viene ripristinata al di sopra dell'altra destinazione). Pertanto, devi utilizzare
getBackStackEntry()
con l'ID della destinazione, per assicurarti di utilizzare sempre
NavBackStackEntry
.
Ciò significa anche che qualsiasi Observer
impostata sul risultato LiveData
verrà
anche quando le destinazioni delle finestre di dialogo sono ancora sullo schermo. Se
Vuoi controllare il risultato solo quando la destinazione della finestra di dialogo è chiusa e
destinazione sottostante diventa la destinazione corrente, puoi osservare
Lifecycle
associato a NavBackStackEntry
e recupera il risultato
solo quando diventa 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) } } }); }
Condividi i dati relativi alla UI tra le destinazioni con ViewModel
Lo stack di navigazione a ritroso
consente di archiviare un
NavBackStackEntry
non solo per ogni singola destinazione, ma anche per ogni navigazione padre
che contiene la singola destinazione. Ciò ti consente di recuperare un
NavBackStackEntry
con ambito a un grafico di navigazione. Un sistema di navigazione
L'elemento NavBackStackEntry
con ambito grafico consente di creare un ViewModel
con ambito a un grafico di navigazione, che ti consente di condividere i dati relativi all'interfaccia utente tra
destinazioni del grafico. Tutti gli oggetti ViewModel
creati in questo modo rimangono attivi fino alla
l'elemento NavHost
associato e i relativi ViewModelStore
vengono cancellati o fino a quando
il grafico di navigazione è saltato fuori dallo stack posteriore.
L'esempio seguente mostra come recuperare un ViewModel
che ha come ambito un
grafico di navigazione:
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);
Se utilizzi Navigatore 2.2.0 o versioni precedenti, devi fornire fabbrica per utilizzare Stato salvato con ViewModels come mostrato nell'esempio seguente:
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());
Per ulteriori informazioni su ViewModel
, vedi
Panoramica del ViewModel.
Modifica di grafici di navigazione gonfiati
Puoi modificare dinamicamente un grafico di navigazione gonfiato al momento dell'attivazione.
Ad esempio, se disponi di una
BottomNavigationView
associato a un NavGraph
, la destinazione predefinita del
NavGraph
indica la scheda selezionata all'avvio dell'app. Tuttavia,
Bisogna ignorare questo comportamento, ad esempio quando una preferenza dell'utente specifica
una scheda preferita da caricare all'avvio dell'app. In alternativa, la tua app
devi modificare la scheda iniziale
in base al comportamento passato degli utenti. Puoi
a questi casi specificando in modo dinamico la destinazione predefinita
NavGraph
.
Considera questo 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>
Quando questo grafico viene caricato, l'attributo app:startDestination
specifica
che HomeFragment
deve essere visualizzato. Per eseguire l'override della destinazione di partenza
in modo dinamico, procedi nel seguente modo:
- Innanzitutto, gonfia manualmente
NavGraph
. - Sostituisci la destinazione di partenza.
- Infine, collega manualmente il grafico a
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);
Ora, all'avvio dell'app, viene visualizzata la dicitura ShopFragment
anziché HomeFragment
.
Quando vengono utilizzati i link diretti, NavController
crea un back stack
automaticamente per la destinazione del link diretto. Se l'utente
accede al link diretto, poi torna indietro, raggiunge la
destinazione a un certo punto. Se esegui l'override della destinazione di partenza utilizzando
dell'esempio precedente, garantisce che l'inizio corretto
viene aggiunta allo stack esistente creato.
Tieni presente che questa tecnica consente anche di ignorare altri aspetti della
NavGraph
in base alle esigenze. Tutte le modifiche al grafico devono essere apportate
prima della chiamata a setGraph()
per verificare che la struttura corretta
viene utilizzato per la gestione dei link diretti, il ripristino dello stato e lo spostamento all'inizio
destinazione del grafico.