了解并实现基本功能

导航是指用户在应用中移动的方式。用户通常通过点按或点击界面元素与界面元素互动,而应用会通过显示新内容来响应。如果用户想返回上一个内容,可以使用返回手势或点按返回按钮。

对导航状态进行建模

模拟此行为的一种便捷方法是使用内容堆叠。当用户向前浏览到新内容时,系统会将其推送到堆栈顶部。当用户从该内容返回时,系统会将其从堆栈中弹出,并显示之前的内容。在导航术语中,此堆栈通常称为返回堆栈,因为它表示用户可以返回的内容。

软件键盘操作按钮(对勾图标)用红色圈出。
图 1. 显示返回堆栈如何随用户导航事件而变化的图表。

创建返回堆栈

在 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 metadataString 键与 Any 值的映射,可提供多样化的数据存储。

如需将 key 转换为 NavEntry,请创建 entryProvider。这是一个接受 key 并为该 key 返回 NavEntry 的函数。在创建 NavDisplay 时,它通常定义为 lambda 参数。

您可以通过以下两种方式创建 entryProvider:直接创建 lambda 函数,或使用 entryProvider DSL。

直接创建 entryProvider 函数

您通常使用 when 语句创建 entryProvider 函数,并为每个键提供一个分支。

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 用于定义具有给定类型和可组合项内容的 NavEntry
  • entry 接受 metadata 参数以设置 NavEntry.metadata

显示返回堆栈

返回堆栈表示应用的导航状态。每当返回堆栈发生变化时,应用界面都应反映新的返回堆栈状态。在 Navigation 3 中,NavDisplay 会观察返回堆栈并相应地更新其界面。使用以下参数构造它:

  • 返回堆栈 - 此项应为 SnapshotStateList<T> 类型,其中 T 是返回堆栈键的类型。它是一个可观察的 List,因此当它发生变化时,会触发 NavDisplay 的重组。
  • 用于将返回堆栈中的键转换为 NavEntryentryProvider
  • (可选)向 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”默认行为。
图 2. 具有两个目的地的 NavDisplay 默认行为。

总结

下图显示了数据如何在 Navigation 3 中的各种对象之间流动:

直观呈现数据如何在 Navigation 3 中的各种对象之间流动。
图 3. 显示数据如何在 Navigation 3 中的各种对象中流动的图表。
  1. 导航事件会发起更改。系统会根据用户互动情况,向返回堆栈添加或从中移除按键。

  2. 返回堆栈状态的更改会触发内容检索NavDisplay(用于渲染返回堆栈的可组合项)会观察返回堆栈。在默认配置下,它会在单个窗格布局中显示最顶层的返回堆栈条目。当返回堆栈上的顶部按键发生变化时,NavDisplay 会使用此按键从条目提供程序请求相应内容。

  3. 条目提供程序提供内容。条目提供程序是一种将键解析为 NavEntry 的函数。从 NavDisplay 收到键后,条目提供程序会提供关联的 NavEntry,其中包含键和内容。

  4. 内容会显示NavDisplay 会接收 NavEntry 并显示内容。