前言
大约在一年半前,我发布了Compose的第一篇文章 Jetpack Compose开篇 之 HelloWorld,连我自己也没想到,这一年半的时间中我竟再也没有看过Compose..., 如今Compose已经发布了稳定版本,还没学会Compose让我的头发又白了许多~
使用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的代码,如下所示:
@Composable
fun 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对象
@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中进行页面跳转了。
普通页面跳转
修改页面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按钮,效果如下图所示。
这样一来,我们就实现了 普通页面跳转,那么 如果我们在页面跳转的时候需要传递参数 ,该如何去做呢?
这里以页面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的更多用法,欢迎持续关注我~