很多Android项目使用Jetpack Navigation进行页面切换。Navigation在设计上高度抽象,只负责导航逻辑不关心页面的具体实现,无论是Activity、Fragment甚至是一个已定义View都可以基于Navigation实现导航。当然,Composable也是可以的。
Jetpack Compose作为一个声明式UI框架经常拿来与React 、Flutter等作对比,但是遗憾的是Compose一直缺少其他框架的导航机制,现在使用Jetpack自家的Navigation,可以将Composable抽象为Destination从而进行导航,补齐了Compose的短板。
Installation
使用navigation-compose,只需要在build.gradle
中添加依赖:
implementation "androidx.navigation:navigation-compose:1.0.0-alpha02"
NavController
Navigation中我们通过findNavController
扩展方法获取NavController,然后进行跳转。
NavController中管理NavGraph等配置信息,所以是stateful的,在Compose的纯函数中需要通过以下方式获取一个有状态的实例
val navController = rememberNavController()
NavHost
NavHost是NavController的持有者,NavHostFragment是Fragment对于NavHost的实现。Compose基于composable函数渲染UI,没有Fragment这样的具体实例做载体,所以Compose的NavHost更加抽象,你可以将其理解为一个容器,内部通过NavController在“页面切换”时,渲染当前UI
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "first_screen"
) {
composable("first_screen") {
// first screen
}
composable("second_screen") {
// second screen
}
}
如上,NavHost接受两个参数,navController和startDestination,这是Navigation的标准用法,不再赘述。其DSL内部的composable
用来声明各个页面
A Navigation Sample
Compose中一个完整的Navigation定义如下:
@Composable
fun ComposeNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "first_screen"
) {
composable("first_screen") {
FirstScreen(navController = navController)
}
composable("second_screen") {
SecondScreen(navController = navController)
}
composable("third_screen") {
ThirdScreen(navController = navController)
}
}
}
配置了三个页面,初始页面是first_screen
composable()
的参数作为Destination的id,用于后续跳转
@Composable
fun FirstScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "First Screen\n" +
"Click me to go to Second Screen",
color = Color.Green,
style = TextStyle(textAlign = TextAlign.Center),
modifier = Modifier.padding(24.dp).clickable(onClick = {
// this will navigate to second screen
navController.navigate("second_screen")
})
)
}
}
如上,FirstScreen中,通过navController.navigate("second_screen")
跳转到SecondScreen。
同样的, 其他的页面定义如下:
@Composable
fun SecondScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Second Screen\n" +
"Click me to go to Third Screen",
color = Color.Yellow,
style = TextStyle(textAlign = TextAlign.Center),
modifier = Modifier.clickable(onClick = {
// this will navigate to third screen
navController.navigate("third_screen")
})
)
}
}
@Composable
fun ThirdScreen(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Third Screen\n" +
"Click me to go to First Screen",
color = Color.Red,
style = TextStyle(textAlign = TextAlign.Center),
modifier = Modifier.clickable(onClick = {
// this will navigate to first screen
navController.navigate("first_screen")
})
)
}
}
最后,需要在setContent中调用ComposeNavigation
:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeNavigationTheme {
ComposeNavigation()
}
}
}
之后,就可以在Compose项目中进行页面切换了,而且还支持BackStack的回退。
Fin
以前如果想使用Compose实现多页面的APP,只能在Fragment或者Activity内部写Compose代码。现在有了Navigation,可以彻底摆脱Fragment或者Activity了,这得益于Navigation高度抽象的设计,有兴趣的同学可以阅读NavController源码了解其中细节。