Compose | UI组件(十三) | Navigation - 页面导航
文章目录
- 前言
- Navaigation 的核心概念和组件的更详细说明
- 真实案例带你一步一步了解 Navaigation
- 第一步,新建多个页面
- 第二步,新建一个Activity
- 第三步,新建一个Screen类,用于统一管理导航的常量
- 第四步,新建一个Graph 统一管理方法
- 第四步,通过 NavGraphBuilder 的扩展函数 navigation 分组
- 第五步,通过 NavGraphBuilder 的扩展函数 分组
- 第六步,跳转的例子
- Main - > Login (各模块间跳转)
- Login - > Signup (单独模块内各页面间跳转)
- SignUp -> Login (返回上一级)
- Login - > Home (返回不同模块的第一个页面)
- Login - > Home -> Detail (返回不同模块上一级,再跳转到不同模块下级页面)
- SignUp -> Home (最顶级页面 跳转到 首页,并且设置为 首页 为 栈顶模式 )
- 总结
前言
关于大前端,不管是Android还是IOS,Web端都会涉及到页面之间的跳转或传值
今天我们要讲的就是Compose中及其重要的组件 Navigation
(页面导航)
Navaigation 的核心概念和组件的更详细说明
Compose Navigation 是一个用于 Jetpack Compose 的导航组件,它提供了简单而强大的 API 来处理页面跳转和用户界面交互。以下是 Compose Navigation 的核心概念和组件的更详细说明:
- NavController:
- 定义:NavController 是导航组件的中心 API,负责控制页面导航的流程。
- 作用:它维护了 Navigation 内部关于页面的堆栈、状态信息、导航图。
- 创建:通过
rememberNavController()
方法创建 NavController 实例。
- NavHost:
- 定义:NavHost 是一个 Composable 函数,用于承载导航的页面,同时也是承载导航页面的容器。
- 参数:NavHost 需要两个必传参数,一个是 NavController,一个是起始路由地址。
- 作用:内部持有 NavController,在页面切换时渲染 UI。通过
composable()
函数构建路线(节点)。
- Screen:
- 定义:Screen 是表示一个单独的、完整的 UI 屏幕的组件。可以将其视为一个页面,包含了一个完整的 UI 布局。
- 创建:通过
Screen
组件创建 Screen 实例。
- Route:
- 定义:Route 是导航路径的抽象表示,用于定义页面之间的跳转关系。
- 创建与管理:通过 NavHost 和 NavController 定义和管理多个 Route。
- Params:
- 定义:Params 是传递给目标 Screen 的数据。
- 传递方式:通过 NavController 的
navigate()
方法传递 Params。在目标 Screen 中接收和使用这些参数。
- Backstack:
- 定义:Backstack 是保存和恢复导航状态的机制。
- 管理方式:通过 NavController 的 backstack 属性,可以管理用户的导航历史,并执行后退操作。
- Transitions:
- 定义:Transitions 是用于页面切换的动画效果。
- 提供与自定义:Compose Navigation 提供了一组默认的过渡动画,也可以自定义过渡效果,以提供流畅的用户体验。
- Lazy Entry:
- 定义:Lazy Entry 是一种懒加载技术,允许延迟加载一些不立即需要的页面。
- 用途:优化性能和资源使用。
- Graph:
- 定义:统一所有的Destination信息,以及可能的跳转路径
- 用途:Navigation需要收集各节点之间的跳转关系,因此
NavDestinotion
需要集中在一起,统一由Graph
管理
- NavLink:类似于 Web 中的锚点,可以用来实现页面内的跳转。
这些核心概念和组件为开发者提供了构建复杂的导航结构和用户界面交互的工具,使得在 Jetpack Compose 应用中实现高效的页面跳转和数据传递变得简单而强大。通过合理使用这些概念和组件,可以创建出具有良好用户体验的应用程序。
真实案例带你一步一步了解 Navaigation
第一步,新建多个页面
多个页面相当于传统View里面的Fragment
HomeScreen.kt
@Composable
fun HomeScreen(navController: NavController){
Column(modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
Text(modifier = Modifier.clickable {},
text = "Home",
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.displayLarge)
Text(modifier = Modifier.padding(top = 150.dp).clickable {},
text = "Login/SinUp",
color = Color.Black,
style = MaterialTheme.typography.headlineMedium)
}
}
LoginScreen.kt
@Composable
fun LoginScreen(navController: NavController){
Column(modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally) {
Text(modifier = Modifier.clickable {},
text = "Login",
color = Color.Magenta,
style = MaterialTheme.typography.displayLarge)
Text(modifier = Modifier.padding(top = 150.dp).clickable {},
text = "Go Back",
color = Color.Black,
style = MaterialTheme.typography.headlineMedium)
}
}
SignUpScreen.kt
@Composable
fun SignUpScreen(navController: NavController){
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text(modifier = Modifier.clickable {},
text = "SingUp",
color = Color.Green,
style = MaterialTheme.typography.displayLarge)
}
}
DetailScreen.kt
@Composable
fun DetailScreen(navController: NavController){
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text(modifier = Modifier.clickable {},
text = "Detail",
color = Color.Red,
style = MaterialTheme.typography.displayLarge)
}
}
第二步,新建一个Activity
class NavigationActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ComposeProjectTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
SetupNavGraph(rememberNavController())
}
}
}
}
}
注:在Activity中写了一个SetupNavGraph方法,该方法就是设置导航的统一管理方法
第三步,新建一个Screen类,用于统一管理导航的常量
Screen.kt
const val AUTHENTICATION_ROUTE = "authentication"
const val ROOT_ROUTE = "root"
const val HOME_ROUTE = "home"
const val HOME = "home_screen"
const val LOGIN = "login_screen"
const val SIGNUP = "signup_screen"
const val DETAIL = "detail_screen"
sealed class Screen(val route:String){
object Home:Screen(route = HOME)
object Login:Screen(route = LOGIN)
object Signup:Screen(route = SIGNUP)
object Detail:Screen(route = DETAIL)
}
第四步,新建一个Graph 统一管理方法
NavGraph.kt
@Composable
fun SetupNavGraph(navController: NavHostController){
NavHost(navController = navController, startDestination = Screen.Home.route){
composable(route = Screen.Home.route){
HomeScreen(navController = navController)
}
composable(route = Screen.Detail.route){
DetailScreen(navController = navController)
}
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
注:
至此,就可以运行项目,显示主页面了。
但是细心的小伙伴,发现了没有,其实我们平时做项目,可能是多模块开发,
所以,我们现在可以把 Home
和 Detail
封装成一模块,把 Login
和 Signup
分成一个模块
第四步,通过 NavGraphBuilder 的扩展函数 navigation 分组
@Composable
fun SetupNavGraph(navController: NavHostController){
NavHost(navController = navController, startDestination = HOME_ROUTE, route = ROOT_ROUTE){
navigation(startDestination = Screen.Home.route,route = HOME_ROUTE){
composable(route = Screen.Home.route){
HomeScreen(navController = navController)
}
composable(route = Screen.Detail.route){
DetailScreen(navController = navController)
}
}
navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
}
注:
看上面代码,其实还可以优化,可以通过kotlin的扩展函数,来做分组
第五步,通过 NavGraphBuilder 的扩展函数 分组
HomeNavGraph.kt
fun NavGraphBuilder.homeNavGraph(navController: NavHostController){
navigation(startDestination = Screen.Home.route,route = HOME_ROUTE){
composable(route = Screen.Home.route){
HomeScreen(navController = navController)
}
composable(route = Screen.Detail.route){
DetailScreen(navController = navController)
}
}
}
HomeNavGraph.kt
fun NavGraphBuilder.authNavGraph(navController: NavHostController){
navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
NavGraph.kt
@Composable
fun SetupNavGraph(navController: NavHostController){
NavHost(navController = navController, startDestination = HOME_ROUTE, route = ROOT_ROUTE){
homeNavGraph(navController = navController)
authNavGraph(navController = navController)
}
}
注:
现在看下Graph类的代码是不是简洁很多,而且子模块的分组逻辑清晰,
上面代码有个注意的点: startDestination = HOME_ROUTE
,这里指向了 navigation
扩展函数的route
的值
第六步,跳转的例子
Main - > Login (各模块间跳转)
HomeScreen
@Composable
fun HomeScreen(navController: NavController){
Column(...) {
Text(modifier = Modifier.clickable {
navController.navigate(AUTHENTICATION_ROUTE)
},
...)
...
}
}
AuthNavGraph.kt
fun NavGraphBuilder.authNavGraph(navController: NavHostController){
navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
composable(route = Screen.Login.route){
LoginScreen(navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
}
注:navController.navigate
指向 navigation
的 route
的值,startDestination
的值是 Screen.Login.route
,因此跳转到了登录页面
Login - > Signup (单独模块内各页面间跳转)
LoginScreen.kt
@Composable
fun LoginScreen(navController: NavController){
Column(...) {
Text(modifier = Modifier.clickable {
navController.navigate(route = Screen.Signup.route)
},
...)
...
}
}
SignUp -> Login (返回上一级)
SignUpScreen.kt
@Composable
fun SignUpScreen(navController: NavController){
Box(...) {
Text(modifier = Modifier.clickable {
navController.popBackStack()
},
...)
}
}
Login - > Home (返回不同模块的第一个页面)
LoginScreen
@Composable
fun LoginScreen(navController: NavController){
Column(...) {
Text(...)
Text(modifier = Modifier.padding(top = 150.dp).clickable {
navController.navigate(HOME_ROUTE){
popUpTo(HOME_ROUTE)
}
},
...)
}
}
注:popUpTo
清除当前栈顶到节点 HOME_ROUTE
之间所有节点(不包含 Screen.Home.route
)
Login - > Home -> Detail (返回不同模块上一级,再跳转到不同模块下级页面)
@Composable
fun LoginScreen(navController: NavController){
Column(...) {
Text(...)
Text(...)
Text(modifier = Modifier.padding(top = 30.dp).clickable {
navController.popBackStack()
navController.navigate(Screen.Detail.route)
},
...)
}
}
注:
navController.popBackStack()
返回不同模块上一级,navController.navigate(Screen.Detail.route)
跳转到不同模块下级页面
SignUp -> Home (最顶级页面 跳转到 首页,并且设置为 首页 为 栈顶模式 )
@Composable
fun SignUpScreen(navController: NavController){
Column(...) {
Text(...)
Text(modifier = Modifier.clickable {
navController.navigate(HOME_ROUTE){
popUpTo(HOME_ROUTE){ inclusive = true}
}
},
...)
Text(modifier = Modifier.clickable {
navController.navigate(HOME_ROUTE){
launchSingleTop
popUpTo(HOME_ROUTE)
}
},
...)
}
}
注:
至此,页面之间跳转的各种情况都讲到了,如有发现其他问题和补充,欢迎反馈和评论区留言
后续关于页面之间的传参,单独写篇文章讲解,后续再会…
总结
- Compose Navigation 是一个用于 Jetpack Compose 的导航组件,它提供了简单而强大的 API 来处理页面跳转 和 用户界面交互
- NavController 是导航组件的中心 API,负责 控制页面导航的流程,通过
rememberNavController()
创建 - NavHost 是一个 Composable 函数,用于 承载导航的页面,同时也是 承载导航页面的容器
- 通过
navController.navigate(route路径)
导航页面 - 通过
navController.popBackStack()
返回上一页面 - 通过
popUpTo
清除 当前栈顶 到 当前节点 之间的 所有节点