NavController
保留一个“返回栈”,其中包含用户已访问的目标位置。当用户在您的应用中浏览屏幕时,NavController
会向返回栈添加和删除目标位置。
作为栈,返回栈是一种“后进先出”数据结构。因此,NavController
将项目推送到栈顶并从栈顶弹出项目。
基本行为
以下是在考虑返回栈行为时应考虑的核心事实
- 第一个目标位置:当用户打开应用时,
NavController
会将第一个目标位置推送到返回栈的顶部。 - 推送到栈:每个调用
NavController.navigate()
会将给定的目标位置推送到栈顶。 - 弹出顶部目标位置:点击向上或返回会分别调用
NavController.navigateUp()
和NavController.popBackStack()
方法。它们会将顶部目标位置从栈中弹出。有关向上和返回之间区别的更多信息,请参阅导航原则页面。
返回
NavController.popBackStack()
方法尝试将当前目标从返回栈中弹出,并导航到上一个目标。这实际上将用户在导航历史记录中向后退一步。它返回一个布尔值,指示它是否成功弹回到目标。
弹出到特定目标
您还可以使用 popBackStack()
导航到特定目标。为此,请使用其重载之一。有几个允许您传入标识符,例如整数 id
或字符串 route
。这些重载将用户带到与给定标识符关联的目标。至关重要的是,它们会弹出该目标上方堆栈上的所有内容。
这些重载还采用 inclusive
布尔值。它确定 NavController
是否也应在导航到指定目标后将其从返回栈中弹出。
考虑以下简短代码片段作为示例
navController.popBackStack(R.id.destinationId, true)
此处,NavController
弹回到具有整数 id destinationId
的目标。由于 inclusive
参数的值为 true
,因此 NavController
还会将给定目标从返回栈中弹出。
处理弹出失败
当 popBackStack()
返回 false
时,后续对 NavController.getCurrentDestination()
的调用将返回 null
。这意味着应用程序已将最后一个目标从返回栈中弹出。在这种情况下,用户只会看到一个空白屏幕。
这可能出现在以下情况下
popBackStack()
没有从堆栈中弹出任何内容。popBackStack()
已将目标从返回栈中弹出,并且堆栈现在为空。
要解决此问题,您必须然后导航到新目标或在您的活动上调用 finish()
以结束它。以下代码片段演示了这一点
Kotlin
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish()
}
Java
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish();
}
弹出到目标
要在从一个目标导航到另一个目标时从返回栈中删除目标,请将 popUpTo()
参数添加到关联的 navigate()
函数调用中。 popUpTo()
指示导航库在调用 navigate()
的过程中从返回栈中删除一些目标。参数值为返回栈上目标的标识符。标识符可以是整数 id
或字符串 route
。
您可以包含 inclusive
参数的参数,其值为 true
,以指示您在 popUpTo()
中指定的目的地也应该从返回栈中弹出。
要在编程中实现这一点,请将 popUpTo()
传递给 navigate()
作为 NavOptions
的一部分,并将 inclusive
设置为 true
。这在 Compose 和 Views 中都有效。
弹出时保存状态
当您使用 popUpTo
导航到某个目标时,您可以选择保存返回栈以及从返回栈中弹出的所有目标的状态。然后,您可以在稍后导航到该目标时恢复返回栈和目标。这使您可以为给定目标保留状态并拥有 多个返回栈。
要以编程方式执行此操作,请在将 popUpTo
添加到导航选项时指定 saveState = true
。
您也可以在导航选项中指定 restoreState = true
以自动恢复返回栈以及与目标关联的状态。
例如
navController.navigate(
route = route,
navOptions = navOptions {
popUpTo<A>{ saveState = true }
restoreState = true
}
)
要在 XML 中启用保存和恢复状态,请分别在关联的 action
中将 popUpToSaveState
定义为 true
,并将 restoreState
定义为 true
。
XML 示例
以下是 XML 中 popUpTo
的示例,使用操作
<action
android:id="@+id/action_a_to_b"
app:destination="@id/b"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"
app:restoreState=”true”
app:popUpToSaveState="true"/>
Compose 示例
以下是 Compose 中相同内容的完整示例
@Composable
fun MyAppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: Any = A
) {
NavHost(
modifier = modifier,
navController = navController,
startDestination = startDestination
) {
composable<A> {
DestinationA(
onNavigateToB = {
// Pop everything up to, and including, the A destination off
// the back stack, saving the back stack and the state of its
// destinations.
// Then restore any previous back stack state associated with
// the B destination.
// Finally navigate to the B destination.
navController.navigate(route = B) {
popUpTo<A> {
inclusive = true
saveState = true
}
restoreState = true
}
},
)
}
composable<B> { DestinationB(/* ... */) }
}
}
@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
Button(onClick = onNavigateToB) {
Text("Go to A")
}
}
更细致地,您可以通过以下方式更改调用 NavController.navigate()
的方式
// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a")
}
// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a") { inclusive = true }
}
// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
launchSingleTop = true
}
有关将选项传递给 NavController.navigate()
的一般信息,请参阅 使用选项导航指南。
使用操作弹出
在使用操作导航时,您可以选择性地从返回栈中弹出其他目标。例如,如果您的应用具有初始登录流程,则在用户登录后,您应该将所有与登录相关的目标从返回栈中弹出,以便“返回”按钮不会将用户带回登录流程。
其他阅读材料
有关更多信息,请阅读以下页面