Navigation与多页面

2026-03-31 21:31 Navigation与多页面已关闭评论

006 Navigation 与多页面:我在 Compose 中的导航实践

今天我想和你聊聊在 Jetpack Compose 中如何处理页面导航。如果你像我一样,是从传统的 Fragment + Navigation Component 迁移过来的,刚开始可能会有点不习惯。但别担心,Compose 的导航本质上还是那个熟悉的 Jetpack Navigation,只是换了一种更“声明式”的写法。

1. 从 Fragment 到 Compose:导航方式的变迁

为了让你快速建立起概念,我整理了一个简单的对照表,看看我们熟悉的 Fragment 导航是如何对应到 Compose 世界的:

我们熟悉的 Fragment + Navigation Component 在 Compose 中的对应物
NavHostFragment + nav_graph.xml 文件 NavHost + Kotlin DSL 或序列化路由
findNavController().navigate(R.id.xxx) navController.navigate("route")
Safe Args 插件生成参数 类型安全路由(Navigation 2.8+ 的 Kotlin DSL / 序列化参数)

核心要记住一点:Compose 导航仍然属于 Jetpack Navigation 家族,它只是提供了一个 Compose 风格的 API,对应的依赖是 navigation-compose

2. 第一步:引入依赖

万事开头先加依赖,这是我的项目里用的版本,你可以根据自己项目的统一版本号进行调整:

implementation("androidx.navigation:navigation-compose:2.8.3")

3. 动手搭建一个最简单的 NavHost

理论说再多,不如动手写一遍。下面是我构建一个基础导航结构时最常用的模式,你可以把它看作一个“最小可行模板”:

import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController

// 1. 定义路由:用密封类管理路由字符串,清晰又安全
sealed class Screen(val route: String) {
    data object Home : Screen("home")
    data object Detail : Screen("detail/{id}") {
        // 一个创建路由字符串的小技巧,避免手滑拼错
        fun create(id: String) = "detail/$id"
    }
}

@Composable
fun AppNav() {
    // 2. 记住导航控制器,它是我们导航的“方向盘”
    val navController = rememberNavController()

    // 3. 构建 NavHost,声明我们的导航图
    NavHost(navController = navController, startDestination = Screen.Home.route) {
        // 首页
        composable(Screen.Home.route) {
            HomeScreen(
                onOpenDetail = { id ->
                    // 触发导航到详情页
                    navController.navigate(Screen.Detail.create(id))
                }
            )
        }
        // 详情页,注意如何从 backStackEntry 中提取参数
        composable(Screen.Detail.route) { backStackEntry ->
            val id = backStackEntry.arguments?.getString("id").orEmpty()
            DetailScreen(
                id = id,
                onBack = { navController.popBackStack() }
            )
        }
    }
}

写了几次之后,我发现手动拼接路由和解析参数还是容易出错。所以我强烈推荐你花点时间看看官方文档的「Type safety」一节,配置好类型安全导航,让编译器来帮你检查,省心很多。

4. 处理更复杂的场景:底部导航栏与多 Tab

这是实际开发中绕不开的难题。一个典型的结构是:顶层用一个 Scaffold 包裹 NavigationBar(底部导航栏),然后每个 Tab 对应一个导航子图。

这里的关键在于如何管理返回栈,避免 Tab 切换时栈混乱。我试过几种方案,目前觉得比较清晰的是为每个 Tab 使用独立的 nested NavHost,或者利用 navigation() DSL 来嵌套子图。

不过导航库的“最佳实践”也在演进,我建议你直接参考官方最新的 Navigation 与多返回栈示例,那里的写法通常是最靠谱的。

5. 混合架构下的导航:从 Activity/Fragment 跳过来

如果你的项目正处于从传统视图向 Compose 迁移的“混合期”,可能会遇到这种情况:

  • 理想情况(纯 Compose 应用):整个 App 就一个 Activity,里面一个 NavHost 管所有页面,非常清爽。
  • 现实情况(混合架构):你可能需要在某个 Fragment 里通过 ComposeView 承载 Compose 页面。这时,可以在这个 ComposeViewsetContent 里内嵌一个 NavHost。如果还需要和外面其他 XML Fragment 跳转,可以通过 Activity 级别的 NavController 或者 Deep Link 来协调通信。虽然有点绕,但能跑通。

6. 别忘了物理返回键

在 Compose 里处理返回键比想象中简单,用 BackHandler 这个 Composable 就行:

BackHandler(enabled = true) {
    // 尝试弹出返回栈
    if (!navController.popBackStack()) {
        // 如果栈已经空了,可以在这里处理,比如 finish Activity
        // 例如:requireActivity().finish()
    }
}

7. 给你的小练习

光看不动手容易忘,我建议你尝试实现下面两个小功能,能帮你巩固理解:

  1. 实现三级导航栈:模拟一个 Home → Detail → DetailSubPage 的流程。体验一下 popBackStack() 的行为,看看它是否和你记忆中 Fragment 导航的表现一致。
  2. 升级到类型安全参数:为上面的 Detail 页面实现类型安全参数。你可以照着官方文档的模板来生成相关代码,感受一下它带来的安全感和便利性。

希望这些经验对你有帮助。导航是应用的骨架,搭好了后面写页面逻辑才会顺畅。

下一篇,我们会聊聊更实际的迁移问题:007-与XML混合与渐进迁移.md

当前文章价值7.88元,扫一扫支付后添加微信提供帮助!(如不能解决您的问题,可以申请退款)

你可能感兴趣的文章

来源:每日教程每日一例,深入学习实用技术教程,关注公众号TeachCourse
转载请注明出处: https://teachcourse.cn/3838.html ,谢谢支持!

资源分享

Andorid开发之反编译工具apktool学习 Andorid开发之反编译工具apktoo
状态管理与ViewModel 状态管理与ViewModel
Python依赖库国内常用镜像源及用法 Python依赖库国内常用镜像源及用
关于接口的理解:源头,接口,终点 关于接口的理解:源头,接口,终

评论已关闭!