当前位置: 首页 > news >正文

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 的核心概念和组件的更详细说明:

  1. NavController
    • 定义:NavController 是导航组件的中心 API,负责控制页面导航的流程。
    • 作用:它维护了 Navigation 内部关于页面的堆栈、状态信息、导航图。
    • 创建:通过 rememberNavController() 方法创建 NavController 实例。
  2. NavHost
    • 定义:NavHost 是一个 Composable 函数,用于承载导航的页面,同时也是承载导航页面的容器。
    • 参数:NavHost 需要两个必传参数,一个是 NavController,一个是起始路由地址。
    • 作用:内部持有 NavController,在页面切换时渲染 UI。通过 composable() 函数构建路线(节点)。
  3. Screen
    • 定义:Screen 是表示一个单独的、完整的 UI 屏幕的组件。可以将其视为一个页面,包含了一个完整的 UI 布局。
    • 创建:通过 Screen 组件创建 Screen 实例。
  4. Route
    • 定义:Route 是导航路径的抽象表示,用于定义页面之间的跳转关系。
    • 创建与管理:通过 NavHost 和 NavController 定义和管理多个 Route。
  5. Params
    • 定义:Params 是传递给目标 Screen 的数据。
    • 传递方式:通过 NavController 的 navigate() 方法传递 Params。在目标 Screen 中接收和使用这些参数。
  6. Backstack
    • 定义:Backstack 是保存和恢复导航状态的机制。
    • 管理方式:通过 NavController 的 backstack 属性,可以管理用户的导航历史,并执行后退操作。
  7. Transitions
    • 定义:Transitions 是用于页面切换的动画效果。
    • 提供与自定义:Compose Navigation 提供了一组默认的过渡动画,也可以自定义过渡效果,以提供流畅的用户体验。
  8. Lazy Entry
    • 定义:Lazy Entry 是一种懒加载技术,允许延迟加载一些不立即需要的页面。
    • 用途:优化性能和资源使用。
  9. Graph:
    • 定义:统一所有的Destination信息,以及可能的跳转路径
    • 用途:Navigation需要收集各节点之间的跳转关系,因此NavDestinotion需要集中在一起,统一由Graph管理
  10. 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)}}
}

注:
至此,就可以运行项目,显示主页面了。

但是细心的小伙伴,发现了没有,其实我们平时做项目,可能是多模块开发,

所以,我们现在可以把 HomeDetail 封装成一模块,把 LoginSignup 分成一个模块

第四步,通过 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 指向 navigationroute 的值,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){launchSingleToppopUpTo(HOME_ROUTE)}},...)}
}

注:
至此,页面之间跳转的各种情况都讲到了,如有发现其他问题和补充,欢迎反馈和评论区留言

后续关于页面之间的传参,单独写篇文章讲解,后续再会…


总结

  1. Compose Navigation 是一个用于 Jetpack Compose 的导航组件,它提供了简单而强大的 API 来处理页面跳转用户界面交互
  2. NavController 是导航组件的中心 API,负责 控制页面导航的流程,通过 rememberNavController() 创建
  3. NavHost 是一个 Composable 函数,用于 承载导航的页面,同时也是 承载导航页面的容器
  4. 通过 navController.navigate(route路径) 导航页面
  5. 通过 navController.popBackStack() 返回上一页面
  6. 通过 popUpTo 清除 当前栈顶当前节点 之间的 所有节点

相关文章:

  • Spring 中获取 Bean 对象的三种方式
  • 十分钟上手vue!
  • Elasticsearch:Geoshape query
  • 基于YOLOv8深度学习的水稻叶片病害智能诊断系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战
  • anaconda+pytorch+pycharm安装总结
  • M1芯片MAC 安装MySQL、Nacos遇到的问题
  • VUE开发记录
  • CTFHub:web-LD_PRELOAD-WP
  • 担心信息泄露被恶意申贷,查大数据报告有用吗?
  • linux中的makefile
  • -1- Python环境安装
  • C++模版初阶
  • 如何在 Golang 中使用 crypto/ed25519 进行数字签名和验证
  • 大数据信用报告查询费用一般要多少钱?
  • 幻兽帕鲁能在Mac上运行吗?幻兽帕鲁Palworld新手攻略
  • [case10]使用RSQL实现端到端的动态查询
  • 【comparator, comparable】小总结
  • If…else
  • Lucene解析 - 基本概念
  • overflow: hidden IE7无效
  • python 装饰器(一)
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • 工作手记之html2canvas使用概述
  • 前端_面试
  • 我这样减少了26.5M Java内存!
  • 小程序开发之路(一)
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • HanLP分词命名实体提取详解
  • ​Linux·i2c驱动架构​
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • (八十八)VFL语言初步 - 实现布局
  • (附源码)ssm学生管理系统 毕业设计 141543
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (剑指Offer)面试题41:和为s的连续正数序列
  • (免费分享)基于springboot,vue疗养中心管理系统
  • (十)T检验-第一部分
  • (十三)Flask之特殊装饰器详解
  • (转) RFS+AutoItLibrary测试web对话框
  • (转载)CentOS查看系统信息|CentOS查看命令
  • ****Linux下Mysql的安装和配置
  • ./和../以及/和~之间的区别
  • .form文件_SSM框架文件上传篇
  • .net core 依赖注入的基本用发
  • .net wcf memory gates checking failed
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET 自定义中间件 判断是否存在 AllowAnonymousAttribute 特性 来判断是否需要身份验证
  • .skip() 和 .only() 的使用
  • [.net]官方水晶报表的使用以演示下载
  • []新浪博客如何插入代码(其他博客应该也可以)
  • [20180129]bash显示path环境变量.txt
  • [2021ICPC济南 L] Strange Series (Bell 数 多项式exp)
  • [CareerCup] 2.1 Remove Duplicates from Unsorted List 移除无序链表中的重复项
  • [codeforces]Levko and Permutation
  • [Flutter]WindowsPlatform上运行遇到的问题总结
  • [IE6 only]关于Flash/Flex,返回数据产生流错误Error #2032的解决方式