一、概念
public fun NavHost( navController: NavHostController, //控制器 startDestination: String, //默认显示的界面(起始页) modifier: Modifier = Modifier, route: String? = null, //用于Navigation嵌套Navigation builder: NavGraphBuilder.() -> Unit //构建导航图 ) |
public fun NavGraphBuilder.composable( arguments: List<NamedNavArgument> = emptyList(), //获取上个界面跳转携带的参数 |
val navController = rememberNavController() //获取控制器。 navController.navigate(“OnePage”) //页面跳转 navController.navigate(“OnePage”) { launchSingleTop = true } //singleTop模式 navController.navigate(“TwoPage”) { popUpTo("OnePage") } navController.navigate(“TwoPage”) { popUpTo("OnePage") { inclusive = true } } navController.popBackStack() //返回上一级(相当于点击返回键)。 |
二、简单跳转
//用单例便于管理路线名称
object RouteConfig {
const val PAGE_ONE = "1"
const val PAGE_TWO = "2"
const val PAGE_THREE = "3"
}
//定义三个页面
@Composable
fun PageOne() {
Box(modifier = Modifier.background(Color.Blue).fillMaxSize().wrapContentSize(align = Alignment.Center)) {
Text(text = "Page One", fontSize = 100.sp, color = Color.White)
}
}
@Composable
fun PageTwo() {
Box(modifier = Modifier.background(Color.Green).fillMaxSize().wrapContentSize(align = Alignment.Center)) {
Text(text = "Page Two", fontSize = 100.sp, color = Color.White)
}
}
@Composable
fun PageThree() {
Box(modifier = Modifier.background(Color.Red).fillMaxSize().wrapContentSize(align = Alignment.Center)) {
Text(text = "Page Three", fontSize = 100.sp, color = Color.White)
}
}
//根节点
@Composable
fun Screen(
modifier: Modifier = Modifier
) {
val navController = rememberNavController() //三个按钮属于外部调用跳转,因此在最外层创建控制器
Column(modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.Top) {
SwitchButton(
modifier = modifier,
onButtonOneClick = { navController.navigate(RouteConfig.PAGE_ONE) },
onButtonTwoClick = { navController.navigate(RouteConfig.PAGE_TWO) },
onButtonThreeClick = { navController.navigate(RouteConfig.PAGE_THREE) }
)
SwitchRegion(modifier = modifier, navController = navController)
}
}
//三个按钮点击切换(状态提升将跳转的处理抛到调用处)
@Composable
fun SwitchButton(
modifier: Modifier = Modifier,
onButtonOneClick: () -> Unit,
onButtonTwoClick: () -> Unit,
onButtonThreeClick: () -> Unit
) {
Row(modifier = modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceAround) {
Button(onClick = onButtonOneClick) { Text(text = "Button One") }
Button(onClick = onButtonTwoClick) { Text(text = "Button Two") }
Button(onClick = onButtonThreeClick) { Text(text = "Button Three") }
}
}
//切换区域
@Composable
fun SwitchRegion(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(), //提供默认实现(如果外部没有调用跳转就不用传了)
startDestination: String = RouteConfig.PAGE_ONE
) {
NavHost(modifier = modifier, navController = navController, startDestination = startDestination) {
composable(route = RouteConfig.PAGE_ONE) { PageOne() }
composable(route = RouteConfig.PAGE_TWO) { PageTwo() }
composable(route = RouteConfig.PAGE_THREE) { PageThree() }
}
}
三、单一可信来源
不管是 NavHost 外部有控制其跳转,还是 NavHost 内部子界面有调用跳转,都应该将跳转抽取成函数形参供调用处处理(外部控制的调用处使用的navController传给NavHost做到状态共享,子界面的调用处是NavHost的composable中),而不是形参传入一个NavHost供自己内部使用。
NavHost(modifier = modifier, navController = navController, startDestination = startDestination) {
composable("profile") {
ProfileScreen(onNavigateToFriends = { navController.navigate("friendsList") })
}
composable("friendslist") { FriendsListScreen() }
}
//子界面有跳转
@Composable
fun ProfileScreen(onNavigateToFriends: () -> Unit) {
Button(onClick = onNavigateToFriends) {
Text(text = "See friends list")
}
}
四、携带参数跳转
界面跳转应该传递简单的必要的数据(如标识ID等),需要传递复杂的数据,应该将这些数据保存在数据层,跳转到新界面后根据ID到数据层获取。通过路线处理参数的结构意味着组合将完全独立于 Navigation 并且更易于测试。
NavHost(startDestination = "pageProfile/{userId}") {
composable(
route = "pageProfile/{userId}", //向路线中添加占位符
arguments = listOf( //往集合中添加参数(NamedNavArgument类型)
navArgument(name = "userId") {
type = NavType.StringType //默认情况下参数都会被解析成字符串,需要指定具体类型
defaultValue = "123456" //默认值(选配)
nullable = false //可否为null(选配)
}
)
){ navBackStackEntry ->
val id = navBackStackEntry.arguments?.getString("userId") //提取参数
}
}
//跳转
navController.navigate("profile/user1234")
五、可选参数
必须使用查询参数语法来添加,必须提供默认值或 nullable = true。
composable(
"pageProfile?userId={userId}",
arguments = listOf(
navArgument("userId") { defaultValue = "user1234" }
)
) { backStackEntry ->
backStackEntry.arguments?.getString("userId")
}
六、深层链接 Deep Link
深层链接可以响应其他界面或外部APP的跳转。
6.1 本应用内跳转
val uri = "https://www.example.com"
composable(
"pageProfile?id={id}",
deepLinks = listOf(navDeepLink { uriPattern = "$uri/{id}" })
) { backStackEntry ->
Profile(navController, backStackEntry.arguments?.getString("id"))
}
6.2 响应外部跳转
<activity …>
<intent-filter>
...
<data android:scheme="https" android:host="www.example.com" />
</intent-filter>
</activity>
6.3 PendingIntent
val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
Intent.ACTION_VIEW,
"https://www.example.com/$id".toUri(),
context,
MyActivity::class.java
)
val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run {
addNextIntentWithParentStack(deepLinkIntent)
getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
}
七、嵌套导航
Navigation中嵌套Navigation。
//一般写法
NavHost(navController, startDestination = "home") {
navigation(startDestination = "username", route = "login") {
composable("username") { ... }
composable("password") { ... }
composable("registration") { ... }
}
}
//建议用扩展函数方便使用
fun NavGraphBuilder.loginGraph(navController: NavController) {
navigation(startDestination = "username", route = "login") {
composable("username") { ... }
composable("password") { ... }
composable("registration") { ... }
}
}
//NavHost中使用
NavHost(navController, startDestination = "home") {
loginGraph(navController)
}