一文搞懂在 Compose 中使用 Navigation 导航 | 开发者说·DTalk

本文介绍如何在Jetpack Compose中使用Navigation组件实现页面间的导航与参数传递,包括必传参数和可选参数的配置方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

446c15bbf06cfe3fc870dc585b4a1a76.jpeg

本文原作者: 黄林晴,原文发布于: Android技术圈

35f718313c4804122b3b788c9268bcab.png

使用 Navigation 在 Compose 中导航

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

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

@Composable
fun 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,如图所示。

f3bfb8fa96626e34d25c59361ff9280a.png

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

定义 NavHost

首先我们定义一个 NavHost 对象:

@Composable
fun 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 代码如下所示: 

@Composable
fun 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 中进行页面跳转了。

6fea925c731f4b4acb2e058905b88512.png

普通页面跳转

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

@Composable
fun 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 按钮,效果如下图所示。

f3f341a40b2de5d8fe1becab19242d4c.gif

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

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

ae4d10d3b40a15040eb3cfd9eff684bc.png

传递参数跳转

必传参数

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

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 按钮,效果如下图所示: 

7d17dd94386df3157cb1457ac2b3ece4.gif

如此一来就实现了从页面 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 按钮,运行结果如下图所示。

cf44264287f11e3c4088fbc35044e258.jpeg

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

180344455467fad0b5ff7114ce37a21d.png

总结

除此之外,Navigation 在 Compose 中还支持深层链接等~ 


长按右侧二维码

查看更多开发者精彩分享

0b3ec9bd3315aa98de761dd20fc94bba.png

"开发者说·DTalk" 面向218d71fc8a6fcd313a6f8c9badf825c4.png中国开发者们征集 Google 移动应用 (apps & games) 相关的产品/技术内容。欢迎大家前来分享您对移动应用的行业洞察或见解、移动开发过程中的心得或新发现、以及应用出海的实战经验总结和相关产品的使用反馈等。我们由衷地希望可以给这些出众的中国开发者们提供更好展现自己、充分发挥自己特长的平台。我们将通过大家的技术内容着重选出优秀案例进行谷歌开发技术专家 (GDE) 的推荐。

0c69e6c25b6aac0fab38d5641b3b2dc1.gif 点击屏末 | 阅读原文 | 即刻报名参与 "开发者说·DTalk" 


8a1aea1bbf509200a956d7a209d997da.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值