写点什么

Jetpack Compose 之 在 Compose 中使用 Navigation 导航

作者:黄林晴
  • 2022 年 3 月 22 日
  • 本文字数:5298 字

    阅读完需:约 17 分钟

 前言

大约在一年半前,我发布了 Compose 的第一篇文章 Jetpack Compose开篇 之 HelloWorld,连我自己也没想到,这一年半的时间中我竟再也没有看过 Compose..., 如今 Compose 已经发布了稳定版本,还没学会 Compose 让我的头发又白了许多~


使用 Navigation 在 Compose 中导航

如果你之前不喜欢 Android 提倡的”单 Activity“应用,那么在 Compose 中相信你会慢慢习惯的~

在此示例中,有两个页面 PageOne 和 PageTwo,首先来看 PageOne 的代码如下所示:

@Composablefun PageOne() {    Column(        modifier = Modifier            .fillMaxWidth()            .fillMaxHeight()            .background(                Color.White            )    ) {        Text(text = "这是页面1")        Spacer(modifier = Modifier.height(20.dp))        Button(onClick = {            //点击跳转到页面2        }) {            Text(                text = "跳转页面2",                modifier = Modifier.fillMaxWidth(),                textAlign = TextAlign.Center            )        }    }}
复制代码


PageOne 页面中有个按钮,点击按钮跳转到页面 2,为了便于区分,添加一个 Text 用来显示当前页面内容。

接着来看 PageTwo 的代码,如下所示:

@Composablefun PageTwo() {    Column(        modifier = Modifier            .fillMaxWidth()            .fillMaxHeight()            .background(                Color.White            )    ) {        Text(text = "这是页面2")        Spacer(modifier = Modifier.height(20.dp))        Button(onClick = {            //点击返回页面1        }) {            Text(                text = "返回页面1",                modifier = Modifier.fillMaxWidth(),                textAlign = TextAlign.Center            )        }    }}
复制代码


MainActivity 代码如下所示:

setContent {    NavigationTheme {        // A surface container using the 'background' color from the theme        Surface(color = MaterialTheme.colors.background) {            PageOne()        }    }}
复制代码


运行程序,显示出页面 1 ,如图所示。



接下来我们来看,如何使用 navigation 来进行页面导航呢?

定义 NavHost

首先我们定义一个 NavHost 对象

@Composablefun NavHostDemo() {
    NavHost(navController =, startDestination =) {
    }
}
复制代码


NavHost 对象需要两个必传参数,一个是 NavController,一个是起始路由地址,NavController 对象是 Navigation 组件的中心 API,我们可以通过 rememberNavController 创建,代码如下所示:

val navController = rememberNavController()
复制代码


为了便于管理路由地址,我们新建 RouteConfig 配置文件,代码如下所示:

object RouteConfig {
    /**     * 页面1路由     */    const val ROUTE_PAGEONE = "pageOne"
    /**     * 页面1路由     */    const val ROUTE_PAGETWO = "pageTwo"}
复制代码


在这里,将页面 1 路由设置为起始导航,并使用 composable 方法添加导航对应关系,修改后的 NavHostDemo 代码如下所示:

@Composablefun NavHostDemo() {
    val navController = rememberNavController()    NavHost(navController = navController, startDestination = RouteConfig.ROUTE_PAGEONE) {
        composable(RouteConfig.ROUTE_PAGEONE) {            OnePage()        }
        composable(RouteConfig.ROUTE_PAGETWO) {            PageTwo()        }    }
}
复制代码


如此一来,我们就建立了导航对应关系,RouteConfig.ROUTE_PAGEONE 对应 OnePage,RouteConfig.ROUTE_PAGETWO 对应 PageTwo,由于我们需要在各自的页面中进行页面跳转,所以将 navController 传递到对应的页面中去,代码如下所示:

composable(RouteConfig.ROUTE_PAGEONE) {    PageOne(navController)}
composable(RouteConfig.ROUTE_PAGETWO) {    PageTwo(navController)}
复制代码


这样我们就可以在页面 1 中进行页面跳转了。

普通页面跳转

修改页面 1 的代码如下所示:

@Composablefun PageOne(navController: NavController) {    Column(        modifier = Modifier            .fillMaxWidth()            .fillMaxHeight()            .background(                Color.White            )    ) {        Text(text = "这是页面1")        Spacer(modifier = Modifier.height(20.dp))        Button(onClick = {            //点击跳转到页面2            navController.navigate(RouteConfig.ROUTE_PAGETWO)        }) {            Text(                text = "跳转页面2",                modifier = Modifier.fillMaxWidth(),                textAlign = TextAlign.Center            )        }    }}
复制代码


我们在按钮的监听事件中调用 navController.navigate 方法,传入页面 2 的路由地址,这样就可以跳转到页面 2 了。在页面 2 中调用 popBackStack 方法将当前页面出栈便又回到了页面 1,这里就不贴页面 2 的代码了。当然我们要记得最后一步:在入口处调用 NavHostDemo()

setContent {    NavigationTheme {        // A surface container using the 'background' color from the theme        Surface(color = MaterialTheme.colors.background) {            NavHostDemo()        }    }}
复制代码


运行程序,点击跳转页面 2 按钮,在页面 2 点击返回页面 1 按钮,效果如下图所示。



这样一来,我们就实现了 普通页面跳转,那么 如果我们在页面跳转的时候需要传递参数 ,该如何去做呢?

这里以页面 1 跳转页面 2 为例,假设页面 1 跳转到页面 2 时需要传递一个 name 参数和 age 参数,该如何去做呢?

传递参数

必传参数

首先,我们定义一个参数配置文件,代码如下所示:

object ParamsConfig {
    /**     * 参数-name     */    const val PARAMS_NAME = "name"
    /**     * 参数-age     */    const val PARAMS_AGE = "age"
}
复制代码


修改 NavHost 的配置,代码如下所示:

NavHost(navController = navController, startDestination = RouteConfig.ROUTE_PAGEONE) {
    composable(RouteConfig.ROUTE_PAGEONE) {        PageOne(navController)    }
    composable(        "${RouteConfig.ROUTE_PAGETWO}/{${ParamsConfig.PARAMS_NAME}}/{${ParamsConfig.PARAMS_AGE}}",        arguments = listOf(            navArgument("$ParamsConfig.PARAMS_NAME") {},            navArgument("$ParamsConfig.PARAMS_AGE") { type = NavType.IntType }        )    ) {        PageTwo(navController)    }}
复制代码


这里,直接将传递的参数使用"/"拼写在路由地址后面添加占位符即可,默认情况下,所有的参数都会被解析成字符串,所以我们可以使用 arguments 来为参数指定 type 类型。当然,因为这里我们只需要将年龄字段指定为整形,所以 navArgument("$ParamsConfig.PARAMS_NAME") {}也可以不写,这里知道就行啦~

那么 PageTwo 页面该如何接收参数呢?

可以通过 composable 函数中提供的 NavBackStackEntry 来获取,并将获取的结果传递给 PageTwo 页面即可,修改后的代码如下所示:

 NavHost(navController = navController, startDestination = RouteConfig.ROUTE_PAGEONE) {
        composable(RouteConfig.ROUTE_PAGEONE) {            PageOne(navController)        }
        composable(            "${RouteConfig.ROUTE_PAGETWO}/{${ParamsConfig.PARAMS_NAME}}/{${ParamsConfig.PARAMS_AGE}}",            arguments = listOf(                navArgument("$ParamsConfig.PARAMS_AGE") { type = NavType.IntType }            )        ) {            val argument = requireNotNull(it.arguments)            val name = argument.getString(ParamsConfig.PARAMS_NAME)            val age = argument.getInt(ParamsConfig.PARAMS_AGE)            PageTwo(name,age,navController)        }    }
复制代码


在 PageTwo 页面接受传递的参数,并添加一个 Text 用于显示,修改后的 PageTwo 的主要代码如下所示:

{        Text(text = "这是页面2")        Spacer(modifier = Modifier.height(20.dp))        Text(text = "我是$name,我今年$age 岁了")        Spacer(modifier = Modifier.height(20.dp))        Button(onClick = {            //点击返回页面1            navController.popBackStack()        }) {            Text(                text = "返回页面1",                modifier = Modifier.fillMaxWidth(),                textAlign = TextAlign.Center            )        }    }
复制代码


最后,在页面 1 的监听事件中使用占位符传参即可,代码如下所示:

{    Text(text = "这是页面1")    Spacer(modifier = Modifier.height(20.dp))    Button(onClick = {        //点击跳转到页面2       navController.navigate("${RouteConfig.ROUTE_PAGETWO}/黄林晴/26")    }) {        Text(            text = "跳转页面2",            modifier = Modifier.fillMaxWidth(),            textAlign = TextAlign.Center        )    }}
复制代码


运行程序,点击跳转页面 1 按钮,效果如下图所示:



 如此一来就实现了从页面 1 到页面 2 的传参,如果我们在页面 1 的点击事件中少传一个参数,会怎么样呢?

navController.navigate("${RouteConfig.ROUTE_PAGETWO}/黄林晴")
复制代码


不用想了,会崩溃!!所以占位符的方式相当于必传参数,如果不传的话则会抛出异常,那么,如果我们想将参数设置为可选参数应该怎么样做呢?

可选参数

可选参数类似于 get 请求的添加方式 ?name = name,现在我们将年龄修改为一个可选参数,来看看如何修改。

首先,我们修改 NavHost 代码如下所示:

composable(RouteConfig.ROUTE_PAGEONE) {            PageOne(navController)        }
        composable(            "${RouteConfig.ROUTE_PAGETWO}/{${ParamsConfig.PARAMS_NAME}}" +                    "?${ParamsConfig.PARAMS_AGE}={${ParamsConfig.PARAMS_AGE}}",            arguments = listOf(                navArgument(ParamsConfig.PARAMS_NAME) {},                navArgument(ParamsConfig.PARAMS_AGE) {                    defaultValue = 30                    type = NavType.IntType                }            )        ) {            val argument = requireNotNull(it.arguments)            val name = argument.getString(ParamsConfig.PARAMS_NAME)            val age = argument.getInt(ParamsConfig.PARAMS_AGE)            PageTwo(name, age, navController)        }
复制代码


当前的路由地址就是“ pageTwo/{name}?age={age}”,由于可选参数必须要设置一个默认值,这里设置年龄的默认值为 30,现在 在页面 1 的点击事件中不再传递年龄参数

navController.navigate("${RouteConfig.ROUTE_PAGETWO}/黄林晴")
复制代码


点击并拖拽以移动

再次运行程序,点击跳转页面 2 按钮,运行结果如下图所示。


由图可知,我们已经成功的将年龄设置为可选参数。

总结

除此之外,Navigation  在 Compose 中还支持深层链接等,关于 Compose 的更多用法,欢迎持续关注我~ 


发布于: 2 小时前阅读数: 5
用户头像

黄林晴

关注

但行好事,莫问前程~ 2018.12.28 加入

Android开发工程师,公众号:"Android技术圈"

评论

发布
暂无评论
Jetpack Compose之 在Compose中使用Navigation导航_android_黄林晴_InfoQ写作平台