Zagnieżdżone wykresy

Procesy logowania, kreatory lub inne podprocesy w aplikacji najlepiej przedstawiać jako zagnieżdżone wykresy nawigacji. Dzięki zagnieżdżaniu zamkniętych podprzepływów nawigacji główny interfejs użytkownika aplikacji jest łatwiejszy do zrozumienia i zarządzania.

Dodatkowo można ponownie używać zagnieżdżonych wykresów. Zapewniają też pewien poziom enkapsulacji – miejsca docelowe spoza zagnieżdżonego grafu nie mają bezpośredniego dostępu do żadnych miejsc docelowych w zagnieżdżonym graficie. Zamiast tego należy użyć funkcji navigate() do samego zagnieżdżonego wykresu, gdzie logika wewnętrzna może się zmieniać bez wpływu na pozostałą część wykresu.

Przykład

Główny diagram nawigacji w aplikacji powinien zaczynać się od początkowego miejsca docelowego, które użytkownik widzi po uruchomieniu aplikacji, i obejmować miejsca docelowe, które widzi podczas poruszania się po aplikacji.

Rysunek 1. Grafika z menu nawigacyjnego najwyższego poziomu.

Na przykładzie grafu nawigacji najwyższego poziomu na rysunku 1 załóżmy, że chcesz, aby użytkownik widział ekrany title_screenregister tylko wtedy, gdy aplikacja jest uruchamiana po raz pierwszy. Następnie informacje o użytkowniku są przechowywane, a przy kolejnych uruchamieniach aplikacji użytkownik powinien od razu przejść do ekranu dopasowania.

Zalecamy, aby ekran poszukiwania ustawić jako punkt początkowy w wykresie nawigacji najwyższego poziomu, a ekrany z tytułami i rejestracją przenieść do zagnieżdżonego wykresu, jak pokazano na rysunku 1:

Rysunek 2. Graf nawigacji najwyższego poziomu zawiera teraz zagnieżdżony graf.

Gdy pojawi się ekran dopasowania, sprawdź, czy jest zarejestrowany użytkownik. Jeśli użytkownik nie jest zarejestrowany, przekieruj go do ekranu rejestracji.

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

Compose

Aby utworzyć zagnieżdżony graf nawigacji za pomocą Compose, użyj funkcji NavGraphBuilder.navigation(). Podczas dodawania miejsc docelowych do grafu możesz używać funkcji navigation() tak samo jak funkcji NavGraphBuilder.composable()NavGraphBuilder.dialog().

Główna różnica polega na tym, że navigation tworzy zagnieżdżoną grafę, a nie nowe miejsce docelowe. Następnie w ramach funkcji lambda usługi navigation() wywołujesz funkcje composable()dialog(), aby dodać miejsca docelowe do grafu zagnieżdżonego.

Zwróć uwagę, jak ten fragment kodu tworzy wykres na rysunku 2 za pomocą usługi Compose:

// 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 przejść bezpośrednio do zagnieżdżonego miejsca docelowego, użyj typu trasy tak jak w przypadku dowolnego innego miejsca docelowego. Dzieje się tak, ponieważ trasy to globalna koncepcja służąca do identyfikowania miejsc docelowych, do których można przejść z dowolnego ekranu:

navController.navigate(route = Match)
.

XML

Jeśli używasz pliku XML, możesz utworzyć zagnieżdżony wykres za pomocą Edytora nawigacji. Aby to zrobić:

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

    Rysunek 2. Grafika zagnieżdżona w Edytorze nawigacji
  3. Kliknij graf zagnieżdżony. W panelu Atrybuty pojawiają się te atrybuty:

    • Typ, który zawiera „Grafikę zagnieżdżoną”.
    • Identyfikator, który zawiera przypisany przez system identyfikator grafu zagnieżdżonego. Ten identyfikator służy do odwoływania się w kodzie do grafu zagnieżdżonego.
  4. Kliknij dwukrotnie osadzony wykres, aby wyświetlić jego miejsca docelowe.

  5. Kliknij kartę Tekst, aby przełączyć się na widok XML. Do wykresu został dodany zagnieżdżony diagram nawigacji. Ten graf nawigacyjny ma własne elementy navigation, własny identyfikator i atrybut startDestination, który wskazuje na pierwszy element docelowy w grafu zagnieżdżonym:

    <?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. W kodzie podaj identyfikator zasobu działania, które łączy graf główny z grafem zagnieżdżonym:

Kotlin

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

Java

Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
  1. Na karcie Projekt możesz wrócić do grafu głównego, klikając Root (Root).

Odwoływanie się do innych grafów nawigacji za pomocą include

Innym sposobem modularyzacji struktury grafu jest uwzględnienie jednego grafu w innym za pomocą elementu <include> w nadrzędnym grafie nawigacji. Dzięki temu można zdefiniować zawarty graf w osobnym module lub projekcie, co zmaksymalizuje możliwość ponownego użycia.

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

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

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