导航描述了用户在应用中移动的方式。用户通常通过点按或点击界面元素与其互动,而应用会通过显示新内容来做出响应。如果用户想要返回上一个内容,可以使用返回手势操作或点按返回按钮。
对导航状态进行建模
对这种行为进行建模的一种便捷方法是使用内容堆栈。当用户向前导航到新内容时,该内容会被推送到堆栈顶部。当用户从该内容返回时,系统会从堆栈中弹出该内容,并显示之前的内容。在导航术语中,此堆栈通常称为返回堆栈,因为它表示用户可以返回的内容。
创建返回堆栈
在 Navigation 3 中,返回堆栈实际上不包含内容。而是包含对内容的引用,称为键。键可以是任何类型,但通常是简单的可序列化数据类。使用引用而非内容具有以下优势:
- 通过将键推送到返回堆栈,可以轻松进行导航。
- 只要键可序列化,返回堆栈就可以保存到永久性存储空间,从而在配置更改和进程终止后继续存在。这一点很重要,因为用户希望在离开应用后,稍后再返回应用时,能够从上次离开的位置继续,并显示相同的内容。如需了解详情,请参阅保存返回堆栈。
Navigation 3 API 中的一个关键概念是,您拥有返回堆栈。该库:
- 预期您的返回堆栈将是快照状态支持的
List<T>,其中T是返回堆栈keys的类型。您可以使用Any,也可以提供自己的强类型键。当您看到“push”或“pop”这两个术语时,底层实现是从列表末尾添加或移除项。 - 观察返回堆栈,并使用
NavDisplay在界面中反映其状态。
以下示例展示了如何创建键和返回堆栈,以及如何修改返回堆栈以响应用户导航事件:
// Define keys that will identify content data object ProductList data class ProductDetail(val id: String) @Composable fun MyApp() { // Create a back stack, specifying the key the app should start with val backStack = remember { mutableStateListOf<Any>(ProductList) } // Supply your back stack to a NavDisplay so it can reflect changes in the UI // ...more on this below... // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state backStack.add(ProductDetail(id = "ABC")) // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state backStack.removeLastOrNull() }
将密钥解析为内容
在 Navigation 3 中,内容使用 NavEntry 进行建模,这是一个包含可组合函数的类。它代表一个目的地,即用户可以向前导航到和向后导航离开的单个内容片段。
NavEntry 还可以包含元数据,即关于内容的信息。此元数据可由容器对象(如 NavDisplay)读取,以帮助它们决定如何显示 NavEntry 的内容。例如,元数据可用于替换特定 NavEntry 的默认动画。NavEntry
metadata 是 String 键到 Any 值的映射,可提供通用的数据存储。
如需将 key 转换为 NavEntry,请创建条目提供方。这是一个接受 key 并返回该 key 的 NavEntry 的函数。创建 NavDisplay 时,它通常定义为 lambda 参数。
您可以通过两种方式创建条目提供程序:直接创建 lambda 函数,或使用 entryProvider DSL。
直接创建条目提供程序函数
您通常使用 when 语句创建条目提供程序函数,并为每个键创建一个分支。
entryProvider = { key -> when (key) { is ProductList -> NavEntry(key) { Text("Product List") } is ProductDetail -> NavEntry( key, metadata = mapOf("extraDataKey" to "extraDataValue") ) { Text("Product ${key.id} ") } else -> { NavEntry(Unit) { Text(text = "Invalid Key: $it") } } } }
使用 entryProvider DSL
entryProvider DSL 可以简化 lambda 函数,因为您无需针对每种密钥类型进行测试,也无需为每种密钥类型构建 NavEntry。
为此,请使用 entryProvider 构建器函数。它还包含默认后备行为(抛出错误),以防找不到密钥。
entryProvider = entryProvider { entry<ProductList> { Text("Product List") } entry<ProductDetail>( metadata = mapOf("extraDataKey" to "extraDataValue") ) { key -> Text("Product ${key.id} ") } }
请注意该代码段中的以下内容:
entry用于定义具有给定类型和可组合项内容的NavEntryentry接受metadata参数来设置NavEntry.metadata
显示返回堆栈
返回堆栈表示应用的导航状态。每当返回堆栈发生变化时,应用界面都应反映新的返回堆栈状态。在 Navigation 3 中,NavDisplay 会观察返回堆栈并相应地更新其界面。使用以下参数进行构建:
- 返回堆栈 - 此参数应为
SnapshotStateList<T>类型,其中T是返回堆栈键的类型。它是一个可观察的List,因此当它发生变化时,会触发NavDisplay的重组。 - 一个
entryProvider,用于将返回堆栈中的键转换为NavEntry对象。 - (可选)为
onBack参数提供 lambda。当用户触发返回事件时,系统会调用此方法。
以下示例展示了如何创建 NavDisplay。
data object Home data class Product(val id: String) @Composable fun NavExample() { val backStack = remember { mutableStateListOf<Any>(Home) } NavDisplay( backStack = backStack, onBack = { backStack.removeLastOrNull() }, entryProvider = { key -> when (key) { is Home -> NavEntry(key) { ContentGreen("Welcome to Nav3") { Button(onClick = { backStack.add(Product("123")) }) { Text("Click to navigate") } } } is Product -> NavEntry(key) { ContentBlue("Product ${key.id} ") } else -> NavEntry(Unit) { Text("Unknown route") } } } ) }
默认情况下,NavDisplay 会在单窗格布局中显示返回堆栈中最顶层的 NavEntry。以下录制内容展示了此应用的运行情况:
NavDisplay 默认行为。目的地生命周期
NavDisplay 使用自定义 LifecycleOwner,根据场景级限制和条目级限制来限制 NavEntry 的生命周期状态。
如需详细了解 Compose 中的生命周期,请参阅 Jetpack Compose 中的生命周期。
场景级生命周期限制
NavDisplay 管理活跃 Scene 的生命周期。场景级上限的确定方式如下:
对于非叠加场景:
对于叠加场景,例如对话框或底部动作条:
RESUMED:仅允许用于最顶层、当前处于活动状态的叠加层场景。STARTED:对于被较新叠加层覆盖的任何底层叠加层场景,上限为STARTED。
条目级生命周期状态
该库会根据每个 NavEntry 在返回堆栈中的存在情况来管理其最大生命周期状态:
RESUMED:如果条目存在于当前返回堆栈中,则其生命周期最多可达到RESUMED(受场景级上限约束)。CREATED:如果条目不再位于返回堆栈中(例如,当条目已弹出,但在动画退出期间仍在屏幕上呈现时),库会将其生命周期严格限制在CREATED。此限制可确保后台或退出条目在完成退出转换时停止执行活跃工作,例如收集流或启动绑定到RESUMED或STARTED状态的协程。
如何组合
例如,NavEntry 的最终生命周期状态解析如下:
| 场景 | 场景级上限 | 入门级上限 | 有效上限 |
|---|---|---|---|
| 活跃条目,稳定屏幕(无过渡或叠加层) | RESUMED |
RESUMED |
RESUMED |
| 过渡期间的活动条目(导航到或导航自) | STARTED |
RESUMED |
STARTED |
| 活动条目,被叠加层覆盖(例如,对话框处于打开状态) | STARTED |
RESUMED |
STARTED |
| 弹出条目,动画退出 | STARTED 或 RESUMED |
CREATED |
CREATED |
综合应用
下图展示了数据如何在 Navigation 3 中的各个对象之间流动:
导航事件会启动更改。系统会根据用户互动在返回堆栈中添加或移除键。
返回堆栈状态的变化会触发内容检索。
NavDisplay(用于呈现返回堆栈的可组合项)会观察返回堆栈。在默认配置中,它会在单窗格布局中显示最顶层的返回堆栈条目。当返回堆栈上的顶部键发生更改时,NavDisplay会使用此键向条目提供程序请求相应内容。条目提供方提供内容。条目提供程序是一个函数,用于将键解析为
NavEntry。从NavDisplay收到密钥后,条目提供程序会提供关联的NavEntry,其中包含密钥和内容。内容会显示。
NavDisplay接收NavEntry并显示内容。