Процессы входа, мастера и другие подпроцессы в приложении лучше всего представлять в виде вложенных графов навигации. Благодаря такому вложению автономных потоков субнавигации основной поток пользовательского интерфейса вашего приложения становится проще для понимания и управления.
Кроме того, вложенные графы можно использовать повторно. Они также обеспечивают уровень инкапсуляции — пункты назначения за пределами вложенного графа не имеют прямого доступа ни к одному из пунктов назначения внутри вложенного графа. Вместо этого они должны navigate() к самому вложенному графу, где внутренняя логика может меняться, не затрагивая остальную часть графа.
Пример
Граф навигации верхнего уровня должен начинаться с экрана, который пользователь видит при запуске приложения, и включать экраны, которые он видит при перемещении по приложению.
В качестве примера рассмотрим граф навигации верхнего уровня, показанный на рисунке 1. Предположим, что вы хотите, чтобы экраны title_screen и register показывались пользователю только при первом запуске приложения. После этого информация о пользователе сохраняется, и при последующих запусках приложения вы должны сразу переходить к экрану match.
Рекомендуется установить экран match в качестве начального пункта назначения графа навигации верхнего уровня и переместить экраны title и register во вложенный граф, как показано на рисунке 1:
Когда откроется экран сопоставления, проверьте, есть ли зарегистрированный пользователь. Если пользователь не зарегистрирован, перенаправьте его на экран регистрации.
Подробнее об условной навигации…
Написать
Чтобы создать вложенный граф навигации с помощью 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 вы можете использовать редактор навигации для создания вложенного графа. Для этого выполните следующие действия:
- В редакторе навигации нажмите и удерживайте клавишу Shift, а затем выберите пункты назначения, которые нужно включить во вложенный граф.
Нажмите правой кнопкой мыши, чтобы открыть контекстное меню, и выберите Move to Nested Graph (Переместить во вложенную диаграмму) > New Graph (Новая диаграмма). Пункты назначения заключены во вложенный граф. На рисунке 2 показан вложенный граф в редакторе навигации.
Рисунок 2. Вложенный граф в редакторе навигации Нажмите на вложенный график. На панели Атрибуты представлены следующие атрибуты:
- Тип, который содержит "Вложенная диаграмма".
- ID – идентификатор, присвоенный системой вложенному графу. Этот идентификатор используется для ссылки на вложенный граф из вашего кода.
Нажмите дважды на вложенный граф, чтобы посмотреть его целевые страницы.
Перейдите на вкладку Текст, чтобы переключиться в режим 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>В коде передайте идентификатор ресурса действия, связывающего корневой граф с вложенным:
Kotlin
view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph)
Java
Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
- На вкладке Design (Дизайн) вернитесь к корневому графу, нажав Root (Корень).
Как ссылаться на другие графы навигации с помощью 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>