应用中的登录流程、向导或其他子流程通常最好用嵌套导航图来表示。通过以这种方式嵌套独立的子导航流程,您可以更轻松地理解和管理应用界面的主要流程。
此外,嵌套图可以重复使用。它们还提供了一定程度的封装,即嵌套图之外的目的地无法直接访问嵌套图中的任何目的地。相反,它们应该 navigate()
到嵌套图本身,在嵌套图中,内部逻辑可以发生更改,而不会影响图的其余部分。
示例
应用的顶级导航图应从用户启动应用时看到的初始目的地开始,并应包含用户在您的应用中移动时看到的目的地。
以图 1 中的顶级导航图为例,假设您希望仅在用户首次启动应用时让其看到 title_screen 和 register 屏幕。之后,系统会存储用户信息。在后续启动应用时,您应将用户直接转到 match 屏幕。
最佳实践是将 match 屏幕设置为顶级导航图的起始目的地,并将标题和注册屏幕移至嵌套图表中,如图 1 所示:
匹配屏幕启动后,检查是否有注册用户。如果用户未注册,请将其转到注册屏幕。
如需详细了解条件导航场景,请参阅条件导航。
Compose
如需使用 Compose 创建嵌套导航图,请使用 NavGraphBuilder.navigation()
函数。在向图表添加目的地时,您可以像使用 NavGraphBuilder.composable()
和 NavGraphBuilder.dialog()
函数一样使用 navigation()
。
主要区别在于,navigation
会创建嵌套图,而不是新的目的地。然后,在 navigation
的 lambda 中调用 composable
和 dialog
,以向嵌套图中添加目的地。
请考虑以下代码段如何使用 Compose 实现图 2 中的图表:
NavHost(navController, startDestination = "title_screen") {
composable("title_screen") {
TitleScreen(
onPlayClicked = { navController.navigate("register") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("register") {
RegisterScreen(
onSignUpComplete = { navController.navigate("gameInProgress") }
)
}
navigation(startDestination = "match", route = "gameInProgress") {
composable("match") {
MatchScreen(
onStartGame = { navController.navigate("in_game") }
)
}
composable("in_game") {
InGameScreen(
onGameWin = { navController.navigate("results_winner") },
onGameLose = { navController.navigate("game_over") }
)
}
composable("results_winner") {
ResultsWinnerScreen(
onNextMatchClicked = {
navController.navigate("match") {
popUpTo("match") { inclusive = true }
}
},
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("game_over") {
GameOverScreen(
onTryAgainClicked = {
navController.navigate("match") {
popUpTo("match") { inclusive = true }
}
}
)
}
}
}
如需直接导航到嵌套目的地,请使用 route
,方法与前往任何其他目的地一样。这是因为路由是一个全局概念,任何屏幕都可以导航到:
navController.navigate("match")
扩展函数
您可以使用 NavGraphBuilder
上的扩展函数向图添加目的地。您可以将这些扩展函数与预构建的 navigation
、composable
和 dialog
扩展方法一起使用。
例如,您可以使用扩展函数添加上一部分演示的嵌套图:
fun NavGraphBuilder.addNestedGraph(navController: NavController) {
navigation(startDestination = "match", route = "gameInProgress") {
composable("match") {
MatchScreen(
onStartGame = { navController.navigate("in_game") }
)
}
composable("in_game") {
InGameScreen(
onGameWin = { navController.navigate("results_winner") },
onGameLose = { navController.navigate("game_over") }
)
}
composable("results_winner") {
ResultsWinnerScreen(
onNextMatchClicked = { navController.navigate("match") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("game_over") {
GameOverScreen(
onTryAgainClicked = { navController.navigate("match") }
)
}
}
}
然后,您可以在传递给 NavHost
的 lambda 中调用此函数,而不是以内嵌方式调用导航。以下示例对此进行了演示:
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = "title_screen") {
composable("title_screen") {
TitleScreen(
onPlayClicked = { navController.navigate("register") },
onLeaderboardsClicked = { /* Navigate to leaderboards */ }
)
}
composable("register") {
RegisterScreen(
onSignUpComplete = { navController.navigate("gameInProgress") }
)
}
// Add the nested graph using the extension function
addNestedGraph(navController)
}
}
XML
使用 XML 时,您可以使用 Navigation Editor 创建嵌套图。为此,请按以下步骤操作:
- 在 Navigation Editor 中,按住 Shift 键,然后点击要包含在嵌套图中的目的地。
右键点击以打开上下文菜单,然后依次选择 Move to Nested Graph > New Graph。目的地包含在嵌套图中。图 2 显示了 Navigation Editor 中的嵌套图:
点击嵌套图表。此时 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">
<strong><include app:graph="@navigation/included_graph" /></strong>
<fragment
android:id="@+id/fragment"
android:name="com.example.myapplication.BlankFragment"
android:label="Fragment in Root Graph"
tools:layout="@layout/fragment_blank">
<strong><action
android:id="@+id/action_fragment_to_second_graph"
app:destination="@id/second_graph" /></strong>
</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"
<strong>android:id="@+id/second_graph"</strong>
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>
其他资源
如需详细了解 Navigation,请参阅下面列出的其他资源: