Вложенные графики

Потоки входа, мастера и другие подпотоки в вашем приложении обычно лучше всего представлять в виде вложенных навигационных графов. Благодаря такой вложенности самостоятельных подпотоков навигации основной поток пользовательского интерфейса вашего приложения становится проще понимать и управлять им.

Кроме того, вложенные графы допускают повторное использование. Они также обеспечивают определённый уровень инкапсуляции — адресаты за пределами вложенного графа не имеют прямого доступа ни к одному из адресатов внутри него. Вместо этого они должны navigate() для перехода к самому вложенному графу, где внутренняя логика может изменяться, не влияя на остальную часть графа.

Пример

Граф навигации верхнего уровня вашего приложения должен начинаться с начального пункта назначения, который пользователь видит при запуске приложения, и должен включать пункты назначения, которые он видит по мере перемещения по приложению.

Рисунок 1. Граф навигации верхнего уровня.

Используя в качестве примера схему навигации верхнего уровня, показанную на рисунке 1, предположим, что вы хотите, чтобы пользователь видел экран заголовка и экраны регистрации только при первом запуске приложения. После этого информация о пользователе сохраняется, и при последующих запусках приложения вы должны сразу перенаправлять его на экран матча .

Рекомендуется установить экран матча в качестве начального пункта назначения графа навигации верхнего уровня и переместить экраны заголовка и регистра во вложенный граф, как показано на рисунке 1:

Рисунок 2. Граф навигации верхнего уровня теперь содержит вложенный граф.

Когда откроется экран матча, проверьте, есть ли зарегистрированный пользователь. Если пользователь не зарегистрирован, переведите его на экран регистрации.

Дополнительную информацию о сценариях условной навигации см. в разделе Условная навигация .

Сочинять

Чтобы создать вложенный навигационный граф с помощью Compose, используйте функцию NavGraphBuilder.navigation() . Функция navigation() используется так же, как функции NavGraphBuilder.composable() и NavGraphBuilder.dialog() при добавлении пунктов назначения в граф.

Основное отличие заключается в том, что navigation создаёт вложенный граф, а не новый пункт назначения. Затем вы вызываете composable() и dialog() внутри лямбда- navigation() , чтобы добавить пункты назначения во вложенный граф.

Рассмотрим, как следующий фрагмент реализует график на рисунке 2 с помощью 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 }
                   }
               }
           )
       }
   }
}

Чтобы перейти непосредственно к вложенному пункту назначения, используйте тип маршрута, как и к любому другому пункту назначения. Это связано с тем, что маршруты — это глобальная концепция, используемая для определения пунктов назначения, к которым можно перейти с любого экрана:

navController.navigate(route = Match)

XML

При использовании XML вы можете создать вложенный график с помощью редактора навигации. Для этого выполните следующие действия:

  1. В редакторе навигации нажмите и удерживайте клавишу Shift и щелкните пункты назначения, которые вы хотите включить во вложенный график.
  2. Щелкните правой кнопкой мыши, чтобы открыть контекстное меню, и выберите «Переместить во вложенный график» > «Новый график» . Целевые объекты заключены во вложенный график. На рисунке 2 показан вложенный график в редакторе навигации :

    Рисунок 2. Вложенный график в редакторе навигации
  3. Щелкните вложенный график. На панели «Атрибуты» появятся следующие атрибуты:

    • Тип , содержащий «Вложенный граф»
    • ID , содержащий системный идентификатор вложенного графа. Этот идентификатор используется для ссылки на вложенный граф из вашего кода.
  4. Дважды щелкните по вложенному графику, чтобы отобразить его пункты назначения.

  5. Перейдите на вкладку «Текст» , чтобы переключиться в режим просмотра XML. Вложенный навигационный граф добавлен в граф. Этот навигационный граф имеет собственные элементы navigation , собственный идентификатор и атрибут startDestination , указывающий на первый пункт назначения во вложенном графе:

    <?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. В вашем коде передайте идентификатор ресурса действия, соединяющего корневой граф с вложенным графом:

Котлин

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

Ява

Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
  1. Вернитесь на вкладку «Конструктор» , вернитесь к корневому графику, нажав кнопку «Корень» .

Ссылайтесь на другие навигационные графики с помощью include

Другой способ модуляризации структуры графа — включение одного графа в другой с помощью элемента <include> в родительском навигационном графе. Это позволяет определить включённый граф в отдельном модуле или проекте, что максимально повышает возможность повторного использования.

В следующем фрагменте показано, как использовать <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>