Zagnieżdżone wykresy

Procesy logowania, kreatory i inne procesy podrzędne w aplikacji są zwykle najlepiej zobrazowane jako zagnieżdżone wykresy nawigacyjne. Dzięki zagnieżdżeniu w ten sposób samodzielnych podnawigacji łatwiej jest zrozumieć główny przepływ interfejsu użytkownika aplikacji i nim zarządzać.

Ponadto zagnieżdżonych wykresów można używać wielokrotnie. Zapewniają też poziom hermetyzacji – miejsca docelowe poza zagnieżdżonym wykresem nie mają bezpośredniego dostępu do żadnego z miejsc docelowych w zagnieżdżonym wykresie. Zamiast tego należy zastosować metodę navigate() w samym zagnieżdżonym wykresie, gdzie logika wewnętrzna może się zmieniać bez wpływu na pozostałą część wykresu.

Przykład

Wykres nawigacyjny najwyższego poziomu aplikacji powinien zaczynać się od miejsca docelowego, które użytkownik widzi po uruchomieniu aplikacji. Powinien zawierać miejsca docelowe, które widzą podczas poruszania się po aplikacji.

Rysunek 1. Wykres nawigacyjny najwyższego poziomu.

Korzystając z przykładowego wykresu nawigacyjnego najwyższego poziomu z Rys. 1, załóżmy, że chcesz, aby użytkownik widział ekrany title_screen i register tylko przy pierwszym uruchomieniu aplikacji. Następnie informacje o użytkowniku są przechowywane. Przy kolejnych uruchomieniach aplikacji należy kierować je bezpośrednio na ekran dopasowania.

Sprawdzoną metodą jest ustawienie ekranu dopasowania jako miejsca docelowego wykresu nawigacyjnego najwyższego poziomu, a następnie przenoszenie ekranów tytułu i rejestracji do zagnieżdżonego wykresu, tak jak to widać na rys. 1:

Rysunek 2. Wykres nawigacyjny najwyższego poziomu zawiera teraz zagnieżdżony wykres.

Po wyświetleniu ekranu dopasowania sprawdź, czy jest zarejestrowany użytkownik. Jeśli użytkownik nie jest zarejestrowany, przejdź do ekranu rejestracji.

Więcej informacji o scenariuszach nawigacji warunkowej znajdziesz w artykule Nawigacja warunkowa.

Utwórz

Aby utworzyć zagnieżdżony wykres nawigacyjny za pomocą opcji Utwórz, użyj funkcji NavGraphBuilder.navigation(). Korzystasz z navigation() tak samo jak NavGraphBuilder.composable() i NavGraphBuilder.dialog() przy dodawaniu miejsc docelowych do wykresu.

Główna różnica polega na tym, że navigation tworzy zagnieżdżony wykres, a nie nowe miejsce docelowe. Następnie wywołujesz metody composable() i dialog() w funkcji lambda funkcji navigation(), aby dodać miejsca docelowe do zagnieżdżonego wykresu.

Zobacz, jak ten fragment implementuje wykres z Rys. 2 za pomocą funkcji Utwórz:

// Routes
@Serializable object Title
@Serializable object Register

// Route for nested graph
@Serializable object Game

// Routes inside nested graph
@Serializable object Match
@Serializable object InGame
@Serializable object ResultsWinner
@Serializable object GameOver

NavHost(navController, startDestination = Title) {
   composable<Title> {
       TitleScreen(
           onPlayClicked = { navController.navigate(route = Register) },
           onLeaderboardsClicked = { /* Navigate to leaderboards */ }
       )
   }
   composable<Register> {
       RegisterScreen(
           onSignUpComplete = { navController.navigate(route = Game) }
       )
   }
   navigation<Game>(startDestination = Match) {
       composable<Match> {
           MatchScreen(
               onStartGame = { navController.navigate(route = InGame) }
           )
       }
       composable<InGame> {
           InGameScreen(
               onGameWin = { navController.navigate(route = ResultsWinner) },
               onGameLose = { navController.navigate(route = GameOver) }
           )
       }
       composable<ResultsWinner> {
           ResultsWinnerScreen(
               onNextMatchClicked = {
                   navController.navigate(route = Match) {
                       popUpTo(route = Match) { inclusive = true }
                   }
               },
               onLeaderboardsClicked = { /* Navigate to leaderboards */ }
           )
       }
       composable<GameOver> {
           GameOverScreen(
               onTryAgainClicked = {
                   navController.navigate(route = Match) {
                       popUpTo(route = Match) { inclusive = true }
                   }
               }
           )
       }
   }
}

Aby nawigować bezpośrednio do zagnieżdżonego miejsca docelowego, użyj typu trasy, tak samo jak w przypadku każdego innego miejsca docelowego. Trasy to koncepcja globalna służąca do identyfikowania miejsc docelowych, do których może przejść każdy z ekranów:

navController.navigate(route = Match)

XML

Jeśli używasz kodu XML, możesz użyć edytora nawigacji do utworzenia zagnieżdżonego wykresu. W tym celu wykonaj następujące czynności:

  1. W edytorze nawigacji naciśnij i przytrzymaj klawisz Shift, a następnie kliknij miejsca docelowe, które chcesz uwzględnić na zagnieżdżonym wykresie.
  2. Kliknij prawym przyciskiem myszy, aby otworzyć menu kontekstowe, i wybierz Przenieś do wykresu zagnieżdżonego > Nowy wykres. Miejsca docelowe są przedstawione na zagnieżdżonym wykresie. Rysunek 2 przedstawia zagnieżdżony wykres w Edytorze nawigacji:

    Rysunek 2. Wykres zagnieżdżony w edytorze nawigacji
  3. Kliknij zagnieżdżony wykres. W panelu Atrybuty są widoczne te atrybuty:

    • Typ zawierający „Wykres zagnieżdżony”.
    • Identyfikator: zawiera przypisany przez system identyfikator zagnieżdżonego wykresu. Ten identyfikator służy do odwoływania się w kodzie do zagnieżdżonego wykresu.
  4. Kliknij dwukrotnie zagnieżdżony wykres, aby wyświetlić jego miejsca docelowe.

  5. Kliknij kartę Tekst, by przełączyć się na widok XML. Do wykresu dodaliśmy zagnieżdżony wykres nawigacyjny. Ten wykres nawigacyjny ma własne elementy navigation, własny identyfikator i atrybut startDestination, który wskazuje pierwsze miejsce docelowe na zagnieżdżonym wykresie:

    <?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. Przekaż w kodzie identyfikator zasobu działania łączącego wykres główny z zagnieżdżonym wykresem:

    Kotlin

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

    kawa

    Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
    
  7. Po powrocie na kartę Projekt wróć do wykresu głównego, klikając Root.

Nawiąż do innych wykresów nawigacyjnych, używając opcji „Uwzględnij”

Innym sposobem modularyzacji struktury wykresu jest uwzględnienie jednego wykresu w innym przy użyciu elementu <include> w nadrzędnym wykresie nawigacyjnym. Dzięki temu uwzględniony wykres można zdefiniować zbiorczo w osobnym module lub projekcie, co maksymalizuje możliwość wielokrotnego wykorzystania.

Ten fragment kodu pokazuje, jak używać <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>

Dodatkowe materiały

Więcej informacji o nawigacji znajdziesz w tych dodatkowych materiałach.

Próbki

Ćwiczenia z programowania

Filmy