Grafici nidificati

Flussi di accesso, procedure guidate o altri flussi secondari all'interno dell'app sono in genere rappresentati al meglio come grafici di navigazione nidificati. Nidificare in questo modo flussi di navigazione secondaria autonomi, per rendere più semplice la comprensione e la gestione del flusso principale dell'interfaccia utente dell'app.

Inoltre, i grafici nidificati sono riutilizzabili. Forniscono inoltre un livello di incapsulamento: le destinazioni al di fuori del grafico nidificato non hanno accesso diretto a nessuna delle destinazioni all'interno del grafico nidificato. Dovrebbero invece navigate() passare al grafico nidificato, dove la logica interna può cambiare senza influire sul resto del grafico.

Esempio

Il grafico di navigazione di primo livello dell'app deve iniziare con la destinazione iniziale che l'utente vede al momento dell'avvio dell'app e deve includere le destinazioni che visualizza mentre si sposta sull'app.

Figura 1. Un grafico di navigazione di primo livello.

Utilizzando come esempio il grafico di navigazione di primo livello della Figura 1, supponiamo che tu voglia richiedere all'utente di visualizzare le schermate title_screen e register solo al primo avvio dell'app. Le informazioni sull'utente vengono memorizzate e, nei lanci successivi dell'app, devi indirizzarle direttamente alla schermata di corrispondenza.

Come best practice, imposta la schermata match come destinazione iniziale del grafico di navigazione di primo livello e sposta le schermate del titolo e della registrazione in un grafico nidificato, come mostrato nella figura 1:

Figura 2. Il grafico di navigazione di primo livello ora contiene un grafico nidificato.

All'avvio della schermata di corrispondenza, verifica se c'è un utente registrato. Se l'utente non è registrato, portalo alla schermata di registrazione.

Per ulteriori informazioni sugli scenari di navigazione condizionale, consulta l'articolo sulla navigazione condizionale.

Scrivi

Per creare un grafico di navigazione nidificato utilizzando Scrivi, utilizza la funzione NavGraphBuilder.navigation(). Utilizzi navigation() come le funzioni NavGraphBuilder.composable() e NavGraphBuilder.dialog() quando aggiungi destinazioni a un grafico.

La differenza principale è che navigation crea un grafico nidificato anziché una nuova destinazione. Quindi chiami composable e dialog nella funzione lambda di navigation per aggiungere destinazioni al grafico nidificato.

Considera come il seguente snippet implementa il grafico nella Figura 2 utilizzando Scrivi:

NavHost(navController, startDestination = "title_screen") {
    composable("title_screen") {
        TitleScreen(
            onPlayClicked = { navController.navigate("register") },
            onLeaderboardsClicked = { /* Navigate to leaderboards */ }
        )
    }
    composable("register") {
        RegisterScreen(
            onSignUpComplete = { navController.navigate("gameInProgress") }
        )
    }
    navigation(startDestination = "match", route = "gameInProgress") {
        composable("match") {
            MatchScreen(
                onStartGame = { navController.navigate("in_game") }
            )
        }
        composable("in_game") {
            InGameScreen(
                onGameWin = { navController.navigate("results_winner") },
                onGameLose = { navController.navigate("game_over") }
            )
        }
        composable("results_winner") {
            ResultsWinnerScreen(
                onNextMatchClicked = {
                    navController.navigate("match") {
                        popUpTo("match") { inclusive = true }
                    }
                },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("game_over") {
            GameOverScreen(
                onTryAgainClicked = {
                    navController.navigate("match") {
                        popUpTo("match") { inclusive = true }
                    }
                }
            )
        }
    }
}

Per navigare direttamente verso una destinazione nidificata, utilizza la route come faresti per qualsiasi altra destinazione. Questo perché le route sono un concetto globale a cui è possibile accedere a qualsiasi schermata:

navController.navigate("match")

Funzioni estensione

Puoi aggiungere destinazioni a un grafico utilizzando una funzione di estensione in NavGraphBuilder. Puoi utilizzare queste funzioni di estensione insieme ai metodi di estensione predefiniti navigation, composable e dialog.

Ad esempio, puoi utilizzare una funzione di estensione per aggiungere il grafico nidificato mostrato nella sezione precedente:

fun NavGraphBuilder.addNestedGraph(navController: NavController) {
    navigation(startDestination = "match", route = "gameInProgress") {
        composable("match") {
            MatchScreen(
                onStartGame = { navController.navigate("in_game") }
            )
        }
        composable("in_game") {
            InGameScreen(
                onGameWin = { navController.navigate("results_winner") },
                onGameLose = { navController.navigate("game_over") }
            )
        }
        composable("results_winner") {
            ResultsWinnerScreen(
                onNextMatchClicked = { navController.navigate("match") },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("game_over") {
            GameOverScreen(
                onTryAgainClicked = { navController.navigate("match") }
            )
        }
    }
}

Puoi quindi chiamare questa funzione nella funzione lambda passata a NavHost anziché chiamare la navigazione in linea. Lo dimostra l'esempio seguente:

@Composable
fun MyApp() {
    val navController = rememberNavController()
    NavHost(navController, startDestination = "title_screen") {
        composable("title_screen") {
            TitleScreen(
                onPlayClicked = { navController.navigate("register") },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("register") {
            RegisterScreen(
                onSignUpComplete = { navController.navigate("gameInProgress") }
            )
        }

        // Add the nested graph using the extension function
        addNestedGraph(navController)
    }
}

XML

Se utilizzi XML, puoi utilizzare l'editor di navigazione per creare il grafico nidificato. Per eseguire questa operazione, procedi nel seguente modo:

  1. Nell'editor di navigazione, tieni premuto il tasto Maiusc e fai clic sulle destinazioni da includere nel grafico nidificato.
  2. Fai clic con il tasto destro del mouse per aprire il menu contestuale e seleziona Sposta nel grafico nidificato > Nuovo grafico. Le destinazioni sono racchiuse in un grafico nidificato. La figura 2 mostra un grafico nidificato nell'editor di navigazione:

    Figura 2. Grafico nidificato nell'editor di navigazione
  3. Fai clic sul grafico nidificato. Nel riquadro Attributi vengono visualizzati i seguenti attributi:

    • Tipo, che contiene "Grafico nidificato"
    • ID, che contiene un ID assegnato dal sistema per il grafico nidificato. Questo ID viene utilizzato per fare riferimento al grafico nidificato dal codice.
  4. Fai doppio clic sul grafico nidificato per visualizzare le relative destinazioni.

  5. Fai clic sulla scheda Testo per passare alla visualizzazione XML. Al grafico è stato aggiunto un grafico di navigazione nidificato. Questo grafico di navigazione ha i propri elementi navigation, il proprio ID e un attributo startDestination che rimanda alla prima destinazione nel grafico nidificato:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        app:startDestination="@id/mainFragment">
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.cashdog.cashdog.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main" >
            <action
                android:id="@+id/action_mainFragment_to_sendMoneyGraph"
                app:destination="@id/sendMoneyGraph" />
            <action
                android:id="@+id/action_mainFragment_to_viewBalanceFragment"
                app:destination="@id/viewBalanceFragment" />
        </fragment>
        <fragment
            android:id="@+id/viewBalanceFragment"
            android:name="com.example.cashdog.cashdog.ViewBalanceFragment"
            android:label="fragment_view_balance"
            tools:layout="@layout/fragment_view_balance" />
        <navigation android:id="@+id/sendMoneyGraph" app:startDestination="@id/chooseRecipient">
            <fragment
                android:id="@+id/chooseRecipient"
                android:name="com.example.cashdog.cashdog.ChooseRecipient"
                android:label="fragment_choose_recipient"
                tools:layout="@layout/fragment_choose_recipient">
                <action
                    android:id="@+id/action_chooseRecipient_to_chooseAmountFragment"
                    app:destination="@id/chooseAmountFragment" />
            </fragment>
            <fragment
                android:id="@+id/chooseAmountFragment"
                android:name="com.example.cashdog.cashdog.ChooseAmountFragment"
                android:label="fragment_choose_amount"
                tools:layout="@layout/fragment_choose_amount" />
        </navigation>
    </navigation>
    
  6. Nel codice, passa l'ID risorsa dell'azione che collega il grafico principale al grafico nidificato:

    Kotlin

    view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph)
    

    java

    Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
    
  7. Torna alla scheda Design e fai clic su Radice per tornare al grafico principale.

Fai riferimento ad altri grafici di navigazione con includi

Un altro modo per modularizzare la struttura del grafico è includere un grafico all'interno di un altro utilizzando un elemento <include> nel grafico di navigazione principale. In questo modo, il grafico incluso può essere definito del tutto in un modulo o un progetto separato, massimizzando così la riusabilità.

Lo snippet seguente mostra come utilizzare <include>:

<!-- (root) nav_graph.xml -->
<?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/fragment">

    <strong><include app:graph="@navigation/included_graph" /></strong>

    <fragment
        android:id="@+id/fragment"
        android:name="com.example.myapplication.BlankFragment"
        android:label="Fragment in Root Graph"
        tools:layout="@layout/fragment_blank">
        <strong><action
            android:id="@+id/action_fragment_to_second_graph"
            app:destination="@id/second_graph" /></strong>
    </fragment>

    ...
</navigation>
<!-- included_graph.xml -->
<?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"
    <strong>android:id="@+id/second_graph"</strong>
    app:startDestination="@id/includedStart">

    <fragment
        android:id="@+id/includedStart"
        android:name="com.example.myapplication.IncludedStart"
        android:label="fragment_included_start"
        tools:layout="@layout/fragment_included_start" />
</navigation>

Risorse aggiuntive

Per scoprire di più sulla navigazione, consulta le seguenti risorse aggiuntive.

Samples

Codelab

Video