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

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

Кроме того, вложенные графы являются многократно используемыми. Они также обеспечивают определенный уровень инкапсуляции — объекты, находящиеся вне вложенного графа, не имеют прямого доступа к каким-либо объектам внутри вложенного графа. Вместо этого они должны 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 , содержащий системный идентификатор вложенного графа. Этот 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)

Java

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>