应用中的登录流程、向导或其他子流程通常最适合表示为嵌套导航图。通过这种方式嵌套独立的子导航流程,应用 UI 的主流程更容易理解和管理。
此外,嵌套图是可重用的。它们还提供了一定程度的封装——嵌套图之外的目标无法直接访问嵌套图内的任何目标。相反,它们应该navigate()
到嵌套图本身,这样内部逻辑可以在不影响图其余部分的情况下进行更改。
示例
您应用的顶级导航图应以用户首次启动应用时看到的初始目标开始,并应包含用户在应用中移动时看到的各个目标。

以图 1 中的顶级导航图为例,假设您希望仅在应用首次启动时才要求用户查看“title_screen”和“register”屏幕。之后,用户信息将被存储,并且在随后的应用启动中,您应该直接将他们带到“match”屏幕。
最佳实践是,将“match”屏幕设置为顶级导航图的起始目标,并将标题和注册屏幕移入嵌套图,如图 1 所示

当匹配屏幕启动时,检查是否存在已注册的用户。如果用户未注册,则将用户导航到注册屏幕。
有关条件导航场景的更多信息,请参阅条件导航。
Compose
要使用 Compose 创建嵌套导航图,请使用 NavGraphBuilder.navigation()
函数。在向图添加目标时,您使用 navigation()
就像使用 NavGraphBuilder.composable()
和 NavGraphBuilder.dialog()
函数一样。
主要区别在于 navigation
创建的是嵌套图,而不是新目标。然后,您可以在 navigation()
的 lambda 中调用 composable()
和 dialog()
以将目标添加到嵌套图。
考虑以下代码片段如何使用 Compose 实现图 2 中的图
// 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. 导航编辑器中的嵌套图 点击嵌套图。以下属性将显示在“Attributes”面板中
- Type,其中包含“Nested Graph”
- ID,其中包含为嵌套图分配的系统 ID。此 ID 用于从代码中引用嵌套图。
双击嵌套图以显示其目标。
点击Text标签页以切换到 XML 视图。一个嵌套导航图已添加到图中。此导航图拥有自己的
navigation
元素以及自己的 ID 和一个指向嵌套图中第一个目标的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>
在代码中,传入将根图连接到嵌套图的操作的资源 ID
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>