该 导航组件 使用一个 导航图 来管理应用的导航。导航图是一种数据结构,其中包含应用中的每个目标以及它们之间的连接。
目标类型
目标主要有三种类型:托管、对话框和活动。下表概述了这三种目标类型及其用途。
类型 |
描述 |
用例 |
---|---|---|
托管 |
填充整个导航主机。也就是说,托管目标的大小与导航主机的大小相同,并且先前目标不可见。 |
主屏幕和详情屏幕。 |
对话框 |
显示覆盖 UI 组件。此 UI 与导航主机的定位或大小无关。先前目标在目标下方可见。 |
警报、选择、表单。 |
活动 |
表示应用中的唯一屏幕或功能。 |
充当导航图的退出点,启动一个新的 Android 活动,该活动与导航组件分开管理。 在现代 Android 开发中,一个应用由一个活动组成。因此,当与第三方活动交互或作为 迁移过程 的一部分时,最好使用活动目标。 |
本文档包含托管目标的示例,托管目标是最常见和最基本的目标。有关其他目标的信息,请参阅以下指南
框架
虽然在每种情况下都适用相同的一般工作流程,但创建导航主机和图的确切方式取决于您使用的 UI 框架。
- Compose: 使用
NavHost
可组合项。使用 Kotlin DSL 将NavGraph
添加到其中。您可以通过两种方式创建图- 作为 NavHost 的一部分: 在添加
NavHost
时直接构建导航图。 - 以编程方式: 使用
NavController.createGraph()
方法创建NavGraph
并将其直接传递给NavHost
。
- 作为 NavHost 的一部分: 在添加
- 片段: 当使用带有视图 UI 框架的片段时,请使用
NavHostFragment
作为宿主。有多种方法可以创建导航图- 以编程方式: 使用 Kotlin DSL 创建
NavGraph
并将其直接应用于NavHostFragment
。- 用于片段和 Compose 的 Kotlin DSL 中使用的
createGraph()
函数是相同的。
- 用于片段和 Compose 的 Kotlin DSL 中使用的
- XML: 直接在 XML 中编写导航宿主和图。
- Android Studio 编辑器: 使用 Android Studio 中的 GUI 编辑器以 XML 资源文件的形式创建和调整导航图。
- 以编程方式: 使用 Kotlin DSL 创建
Compose
在 Compose 中,使用可序列化对象或类来定义一个 *路由*。路由描述了如何到达目标,并包含目标所需的所有信息。定义好路由后,使用 NavHost
可组合函数来创建导航图。请考虑以下示例
@Serializable
object Profile
@Serializable
object FriendsList
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Profile) {
composable<Profile> { ProfileScreen( /* ... */ ) }
composable<FriendsList> { FriendsListScreen( /* ... */ ) }
// Add more destinations similarly.
}
- 一个可序列化对象表示两个路由中的每一个,
Profile
和FriendsList
。 - 对
NavHost
可组合函数的调用传递了一个NavController
和起始目标的路由。 - 传递给
NavHost
的 lambda 最终会调用NavController.createGraph()
并返回一个NavGraph
。 - 每个路由都作为类型参数提供给
NavGraphBuilder.composable<T>()
,它将目标添加到生成的NavGraph
中。 - 传递给
composable
的 lambda 是NavHost
为该目标显示的内容。
了解 lambda
为了更好地理解创建 NavGraph
的 lambda,请考虑,为了构建与前面代码片段相同的图,可以单独使用 NavController.createGraph()
创建 NavGraph
,并将其直接传递给 NavHost
val navGraph by remember(navController) {
navController.createGraph(startDestination = Profile)) {
composable<Profile> { ProfileScreen( /* ... */ ) }
composable<FriendsList> { FriendsListScreen( /* ... */ ) }
}
}
NavHost(navController, navGraph)
传递参数
如果需要将数据传递给目标,请使用具有参数的类定义路由。例如,Profile
路由是一个具有 name
参数的数据类。
@Serializable
data class Profile(val name: String)
每当需要将参数传递给该目标时,都会创建一个路由类的实例,并将参数传递给类构造函数。
对于可选参数,创建具有默认值的空值字段。
@Serializable
data class Profile(val nickname: String? = null)
获取路由实例
可以使用 NavBackStackEntry.toRoute()
或 SavedStateHandle.toRoute()
获取路由实例。使用 composable()
创建目标时,NavBackStackEntry
可作为参数使用。
@Serializable
data class Profile(val name: String)
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(name = profile.name) }
}
请注意此代码片段中的以下内容
Profile
路由指定导航图中的起始目标,"John Smith"
作为name
的参数。- 目标本身是
composable<Profile>{}
代码块。 ProfileScreen
可组合函数获取profile.name
的值作为其自己的name
参数。- 因此,值
"John Smith"
传递给ProfileScreen
。
最小示例
NavController
和 NavHost
协同工作的完整示例
@Serializable
data class Profile(val name: String)
@Serializable
object FriendsList
// Define the ProfileScreen composable.
@Composable
fun ProfileScreen(
profile: Profile
onNavigateToFriendsList: () -> Unit,
) {
Text("Profile for ${profile.name}")
Button(onClick = { onNavigateToFriendsList() }) {
Text("Go to Friends List")
}
}
// Define the FriendsListScreen composable.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
Text("Friends List")
Button(onClick = { onNavigateToProfile() }) {
Text("Go to Profile")
}
}
// Define the MyApp composable, including the `NavController` and `NavHost`.
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = Profile(name = "John Smith")) {
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(
profile = profile,
onNavigateToFriendsList = {
navController.navigate(route = FriendsList)
}
)
}
composable<FriendsList> {
FriendsListScreen(
onNavigateToProfile = {
navController.navigate(
route = Profile(name = "Aisha Devi")
)
}
)
}
}
}
如代码片段所示,不要将 NavController
传递给可组合函数,而是向 NavHost
公开一个事件。也就是说,可组合函数应该有一个类型为 () -> Unit
的参数,NavHost
为其传递一个调用 NavController.navigate()
的 lambda。
片段
如前面各节所述,当使用片段时,可以选择使用 Kotlin DSL、XML 或 Android Studio 编辑器以编程方式创建导航图。
以下各节详细介绍了这些不同的方法。
以编程方式
Kotlin DSL 提供了一种以编程方式创建带有片段的导航图的方法。在许多方面,这比使用 XML 资源文件更简洁、更现代。
请考虑以下示例,它实现了一个双屏导航图。
首先需要创建 NavHostFragment
,它 *不能* 包含 app:navGraph
元素
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
接下来,将 NavHostFragment
的 id
传递给 NavController.findNavController
。这将 NavController
与 NavHostFragment
关联起来。
随后,对 NavController.createGraph()
的调用将图链接到 NavController
,从而也链接到 NavHostFragment
@Serializable
data class Profile(val name: String)
@Serializable
object FriendsList
// Retrieve the NavController.
val navController = findNavController(R.id.nav_host_fragment)
// Add the graph to the NavController with `createGraph()`.
navController.graph = navController.createGraph(
startDestination = Profile(name = "John Smith")
) {
// Associate each destination with one of the route constants.
fragment<ProfileFragment, Profile> {
label = "Profile"
}
fragment<FriendsListFragment, FriendsList>() {
label = "Friends List"
}
// Add other fragment destinations similarly.
}
以这种方式使用 DSL 与前面关于 Compose 的部分中概述的工作流程非常相似。例如,在那里和这里,NavController.createGraph()
函数都会生成 NavGraph
。同样,虽然 NavGraphBuilder.composable()
将可组合目标添加到图中,但这里 NavGraphBuilder.fragment()
添加了一个片段目标。
有关如何使用 Kotlin DSL 的更多信息,请参阅 使用 NavGraphBuilder DSL 构建图。
XML
您可以直接编写 XML。以下示例反映了并等同于上一节中的双屏示例。
首先,创建一个 NavHostFragment
。它充当包含实际导航图的导航宿主。
NavHostFragment
的最小实现
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/nav_graph" />
</FrameLayout>
NavHostFragment
包含属性 app:navGraph
。使用此属性将导航图连接到导航宿主。以下是如何实现图的示例
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/profile">
<fragment
android:id="@+id/profile"
android:name="com.example.ProfileFragment"
android:label="Profile">
<!-- Action to navigate from Profile to Friends List. -->
<action
android:id="@+id/action_profile_to_friendslist"
app:destination="@id/friendslist" />
</fragment>
<fragment
android:id="@+id/friendslist"
android:name="com.example.FriendsListFragment"
android:label="Friends List" />
<!-- Add other fragment destinations similarly. -->
</navigation>
使用操作来定义不同目标之间的连接。在此示例中,profile
片段包含一个导航到 friendslist
的操作。有关更多信息,请参阅 使用导航操作和片段。
编辑器
可以使用 Android Studio 中的导航编辑器管理应用的导航图。这本质上是一个 GUI,可用于创建和编辑 NavigationFragment
XML,如上一节所示。
有关更多信息,请参阅 导航编辑器。
嵌套图
还可以使用嵌套图。这涉及将图用作导航目标。有关更多信息,请参阅 嵌套图。
进一步阅读
有关更多核心导航概念,请参阅以下指南
- 概述: 确保阅读导航组件的总体概述。
- 活动目标: 如何实现将用户带到活动的示例。
- 对话框目标: 如何创建将用户带到对话框的示例。
- 导航到目标: 涵盖如何从一个目标导航到另一个目标的详细指南。
- 嵌套图: 关于如何在另一个导航图中嵌套一个导航图的深入指南。