应用中的页面跳转是一个常规任务, Google官方提供的解决方案是Android Jetpack的Navigation component.
本文概括介绍一下基本使用的关键点(详细的how to guide看官方就好了),
结合源码梳理一下基本的navigation component的设计, 帮助大家更好地理解和使用这个库.
首先, 官网的介绍很全面了: https://developer.android.com/guide/navigation
如果想按步骤操作一番请移步官方文档.
这里表扬一下Android Studio, 越来越人性化了.
在添加navigation资源的时候会自动加依赖.
Navigation Editor可以显示destination, 拖拽, 连线加action, 添加编辑参数, 设置动画和返回行为等属性, 提供了一个集中可视化的图.
navigation文件夹下, 其中包含了各个destinations.NavHost: 一个空的container, 用来展示destinations. Navigation component有一个默认的NavHost实现: NavHostFragment, 用来展示fragment.NavController: 用来管理navigation. 当告诉NavController想要navigate去哪里, 它就会在NavHost中显示对应的destination.从Navigation Component推出之初的宣传视频, 比如这个, 可以看出它和single activity的思想是紧密结合的.
所以官方推荐的经典做法是这样:
一个activity和多个fragments: activity关联一个navigation graph, 包含一个NavHostFragment, 用来放置不同的fragments.

当然具体的应用可以选择自己想要的方式, 适合自己的才是最好的.
如果有多个activity, 那么每个activity有自己的navigation graph.
以这个简单的例子举例:

如果Login和Main页面是两个Activity, 它们各自的layout里都有一个NavHostFragment, 这样做的目的有两个:
获取NavController的几种方式.)它们又都有各自的navigation graph, 里面列出了可以到达的结点.
因为我们只能到达在同一个graph中列出的节点.
这里LoginActivity需要跳转到MainActivity, 所以在navigation graph中有mainActivity的destination结点.
如果MainActivity也需要跳转到LoginActivity, 就需要在自己的navigation graph中增加一个loginActivity的destination结点.
有一个具体的应用case是, 如果app的主要入口是非全屏的(有共享UI部分, 比如bottom bar), 而部分页面需要全屏, 应该如何处理.
比较简单的一种方式就是如上面的例子, 把全屏的页面放在一个单独的Activity. 但这样就会导致很多Activity的出现.
另外一种方式是动态处理nav host和bottom navigation的布局.
比如需要显示一个全屏的Fragment的时候, bottom bar消失, nav host布局充满屏幕.
这就涉及到一些UI的操作和恢复, 可能还需要动画过渡.
当项目慢慢变大之后, 我们会拆分module来组织代码, 除了基础组件的拆分, 各个feature也可能会拆到不同的module中去.
官方建议的方式, 如图所示, app module作为总入口, 依赖feature modules.
navigation graph也放在app module中.

因为navigation graph是支持嵌套和include的, 即navigation里面也可以嵌套navigation, 子的navigation有自己的start destination.
所以navigation graph也可以拆分, 各个module管理自己的navigation graph, 最终include到app module中去.
跨module导航的行为, 是deep link的方式.
具体代码见navigation-multi-module
源码实现是通过字符串匹配找到destination, 然后根据具体的类型找到navigator进行导航.
需要注意, 即便是app module, 它想导航到一个比较深的结点, 推荐的方式也是通过deep link.
当我们嵌套navigation时, 总navigation图的可见结点只到子graph为之, 其内部结点都不可见, 导航会发生destination找不到的错误.
大多数情况, app module也只关心几个入口结点.
跨module导航还有一个缺点是safe args不支持.

NavHost接口的唯一实现类是:NavHostFragment.
在NavHostFragment中创建了NavController, 这里也是所有方法最终获得到的NavController的来源.
通过Fragment的生命周期onCreate()触发了graph的创建.
NavController负责了导航行为的控制.
NavController中有很多navigate()方法的重载, 可以根据不同的参数进行导航.
popBackStack()是回退操作.
最终的实现都是从destination中获取到navigator的名字, 然后调用具体的Navigator的navigate()或popBackStack()方法.
NavHostController是NavController的子类, 提供了一些连接外部依赖的设置方法.
App通常不会构造controller, 而是从navigation host获取.
NavController中有字段NavigatorProvider, 而NavigatorProvider中有一个navigators的HashMap.

NavDestination是一个描述不同目的地的数据结构基类.
具体实现在不同类型的Navigator中都有对应的类.
NavGraph也是NavDestination的子类. 只不过NavGraph中记录了destination节点信息.
Navigator是一个抽象类.
包含的方法中对应导航行为和回退行为的是:
navigate()popBackStack()createDestination()的方法负责了destination的创建.下面几种子类: 对应不同destination的导航.
ActivityNavigator.FragmentNavigator.DialogFragmentNavigator.这个子类:
NavGraphNavigator. 是一个针对NavGraph的元素. 会导航到graph的start destination. 当然具体导航行为会由具体元素类型的provider执行.可以查看这几个类的导航实现.
比如点进FragmentNavigator的navigate()方法实现, 我们就会发现最终执行的是replace()操作.
Navigation component是支持自定义Navigator的, 我们可以仿照这个类写出自己的版本, 达到定制化的目的.
导航的setup过程大致如下:

这里展示的是xml的navigation graph, 其中解析xml的工作由NavInflator来完成.
解析完成后由navigator进行具体的destination类型创建.
这里graph创建完成之后还会导航到start destination.
要跳转到具体某个destination时, 流程如下:

这里解释了为什么只能导航到同一个图下的目的地.
以及最终的导航动作, 是找到对应destination的navigator实现来进行的.
这样对NavController来说就不必关心具体实现.
获取NavController的方式有三种(先不说Compose).
fun Activity.findNavController(@IdRes viewId: Int): NavController =
Navigation.findNavController(this, viewId)
参数传入view的id. 之后会调用findViewNavController()
fun Fragment.findNavController(): NavController =
NavHostFragment.findNavController(this)
首先向根部遍历, 找到NavHostFragment, 然后getNavController().
找不到还会尝试在view中找, 或者在dialog的view中找.
当然如果拿得到NavHostFragment可以直接get.
fun View.findNavController(): NavController =
Navigation.findNavController(this)
最后的本质依然是调用到了findViewNavController().
不断递归找view的parent, 然后getNavController, 找到为止.
这个地方NavController是写在View的tag里.
查了一下这个方法的调用是NavHostFragment的onViewCreated()里.

所以以上提到的这三种方式, 归根结底是要找到NavHostFragment中的那个NavController.
navigation component还提供了DSL的方式来声明graph, 取代xml的版本.
这种方式可以用于动态构建一个navigation graph.
代码看起来像这样:
val navController = findNavController(R.id.nav_host_fragment)
navController.graph = navController.createGraph(
startDestination = mav_routes.home
) {
fragment<HomeFragment>(nav_routes.home) {
label = resources.getString(R.string.home_title)
}
fragment<PlantDetailFragment>(${nav_routes.plant_detail}/${nav_arguments.plant_id}) {
label = resources.getString(R.string.plant_detail_title)
argument(nav_arguments.plant_id) {
type = NavType.StringType
}
}
}
DSL方式的局限性也是不能和safe args结合.
Compose版本的navigation包是: androidx.navigation:navigation-compose.
有了前面的铺垫, 我们可以发现compose导航库的实现是DSL版本的写法, 结合新的ComposeNavigator.

NavHost(navController = navController, startDestination = "profile") {
composable("profile") { Profile(/*...*/) }
composable("friendslist") { FriendsList(/*...*/) }
/*...*/
}
所以同样的, NavController要和一个NavHost关联, NavHost其中有一个navigation graph定义了所有的destinations.
每个destination有一个唯一的route字符串来定义自己的路径.
navigation graph同样也可以嵌套.
并且和View的Navigation Component是有Interoperability支持的.
Navigation Component是一个很基础却很有意思的库.
它封装了导航行为, 方便了开发者调用, 也解耦了导航动作和具体结点的实现类.
解决了参数传递的类型安全问题.
提供了可视化的导航图编辑预览工具.
提供了导航UI组件并提供了默认行为, 让开发者直接获得符合设计的默认效果.
它的设计跟单Activity的架构相关, 支持拓展destination类型, 支持dsl写法.
本文结合源码讨论了一下这个库的设计和使用的关键点, 希望对大家有帮助.
一文解决关于VLAN所有的疑惑VLAN基本概念为什么需要VLAN?怎么在交换机上划分VLAN,VLAN的工作原理有了子网,已经隔离了广播,还需要VLAN干啥?只进行子网划分,不进行VLAN划分VLAN划分与子网划分附加VLAN信息的方法VLAN划分交换机的端口类型(Access和Trunk)一、访问链接二、汇聚链接汇聚链接VLAN间通信为什么要进行VLAN间通信?路由器实现VLAN间通信路由器和交换机的连接方式通信细节三层交换机实现VLAN间通信加速VLAN间通信三层交换机与路由器三层交换机路由器路由器和交换机配合构建LAN的实例使用VLAN设计局域网的特点VLAN增加网络的灵活性不使用VLA
✅作者简介:大家好,我是小杨📃个人主页:「小杨」的csdn博客🔥系列专栏:小杨带你玩转C语言【初阶】🐳希望大家多多支持🥰一起进步呀!大家好呀!我是小杨。小杨花几天的时间将C语言中的操作符这部分知识做了一个大总结,在方便自己复习的同时也能够帮助到大家。通篇字数在一万字左右,可以算作是非常详细了,一文就可以带领大家彻底掌握操作符这部分内容,文章很长建议先收藏再看,防止下次想看就找不到啦。文章目录✍1,算术操作符✍2,移位操作符 🔍2.1,左移操作符 🔍2.2,右移操作符 ✨2.2.1,算术移位 ✨2.2.2,逻辑移位✍3,位操作符 🔍3.1,按位与&
每个企业都希望在完成项目后获得盈利,但不少企业到了年终后才发现项目做了不少,公司却并没能达到预期,甚至还出现了亏损。那么钱究竟去了哪里?很多公司都搞不清楚原因,出现糊涂账较多的状况,这将会造成严重的后果,尤其在疫情影响下,大环境很恶劣,如果是大公司的事业部门出现亏损,就可能会导致事业部门解散;如果是小公司出现亏损,就很容易导致公司倒闭;怎样做才能确保我们所完成的项目都能获利?从财务角度看,要确保盈利必须做到合理估算成本,只有这样才能在对外签订合约时做出合理报价,在对内在开始项目前做出充分评估投入代价,同时在实施过程中还要控制成本得当,最后项目结束时才会有可能获得盈利。那么我们怎样才能准确的判断
COINDAO旨在重建社区信任和安全。基于皖北基因的强烈共识,COINDAO自发产生了一个共创、共建、共治、共享的协作组织。它专注于DAO投资管理协议,为新的优质项目创造增长技术和资金。COINDAO的使命就是为真正的优质项目打造一个去中心化、公开透明的平台,让各个赛道上的优质项目能够以更低的成本快速募集资金并向公众开放.打破头部垄断。让真正的爱好者直接获得早期参与优质项目的资格,不再遥不可及,构建生态应用的可信体系,打造人人参与共建、人人共享的去中心化DAO好处。生态系统,我们称之为COINDAO生态系统。COINDAO国内各大财经网站宣发如下:COINDAO国外各大财经网站宣发: COIN
今天来说说前端低代码有多幸福?低代码是啥?顾名思义少写代码……这种情况下带来的幸福有:代码写得少,bug也就越少(所谓“少做少错”),因此开发环节的两大支柱性工作“赶需求”和“修bug”就都少了;要测的代码少了,那么测试用例也可以少写了。所以,总结低代码带来的幸福感有这三大点:开发效率提高开发成本减少维护性更高针对上述三点,我们展开说说。01、开发效率提高对于低代码的理解,个人认为可以通过配置化的低成本交互方式(主流是拖拽)加上少量的胶水代码,去满足一类应用的需求。这就说明,基于低代码,开发人员无需代码或说只需少量代码就可以开发出各类应用管理系统,如:OA协同办公、KM知识管理、CRM客户关系
目录🍊前言🍊:🍈一、宏与函数🍈: 1.宏与函数对比: 2.宏与函数的命名约定:🍓二、预处理操作符🍓: 1.预处理操作符"#": 2.预处理操作符"##":🥝三、条件编译🥝: 1.简述条件编译指令: 2.常见条件编译指令:🍒总结🍒:🛰️博客主页:✈️銮同学的干货分享基地🛰️欢迎关注:👍点赞🙌收藏✍️留言🛰️系列专栏:💐【进阶】C语言学习 🧧 C语言学习🛰️代码仓库:🎉VS2022_C语言仓库 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!
作者:翟天保Steven版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处一、设计模式是什么? 设计模式是为了解决在软件开发过程中遇到的某些问题而形成的思想。同一场景有多种设计模式可以应用,不同的模式有各自的优缺点,开发者可以基于自身需求选择合适的设计模式,去解决相应的工程难题。 良好的软件设计和架构,可以让代码具备良好的可读性、可维护性、可扩展性、可复用性,让整个系统具备较强的鲁棒性和性能,减少屎山代码出现的概率。 想要熟练运用设计模式,提高自己的编程能力和架构能力,只有在自己工作中,结合自身工作内容,多思考多实践。本文只能通过举一些通俗的例子,来
自今年4月以来,在经历了UST脱锚,所引发的头部投资机构、独角兽CeFi平台的相继“破产”,加密行业迎来了前所未有的发展危机,一方面在于市场信心不足甚至恐慌,导致的资金加速出逃,另一方面市场整体增速缓慢导致各个板块收益疲软,导致投资者们难以获得可观的收益回报,市场进一步陷入新一轮的恶性循环。当然,即便是市场整体处于下行周期,但加密市场仍旧存在诸多的获利机会,新型LaaS协议ElephantSwap正在通过其独特的LaaS方案,来为DeFi市场恢复流动性注入信心,同时为投资者带来十分可观的套利空间。目前,PlatoFarm是首个使用ElephantSwap流动性服务的项目,投资可以通过将手中的P
系列文章目录第一章Android:彻底搞懂Lifecycle——使用篇第二章Android:彻底搞懂Lifcycle——原理篇文章目录系列文章目录前言一、Lifecycle是什么?1.应用场景2.示例二、Lifecycle使用1.泳(用)裤(库)第一步是什么?——先引入2.Lifecycle类3.LifecycleOwner接口3.1.自定义类实现LifecycleOwner接口总结前言你真的了解lifecycle吗?本文的目标就是要“打破砂锅问到底”,带你真真切切了解它。一、Lifecycle是什么?lifecycle是属于AndroidJetpack(官方开发工具包)——Architect
一、简介1.1Flyway是什么?Flyway是一款开源的数据库版本管理工具,可以实现管理并跟踪数据库变更,支持数据库版本自动升级,而且不需要复杂的配置,能够帮助团队更加方便、合理的管理数据库变更。例:创建两个sql变更文件,项目启动后会将两个文件中的sql语句全部执行。1.2为什么使用Flyway?简单举个例子:开发时,如果A开发和B开发都对同一数据库进行了修改,那么如何进行数据同步呢?假如多个开发人员都修改了sql脚本,怎么同步到测试环境和生产环境?类似于以上的情况在日常开发中不胜枚举,在最开始的单体架构中,我们公司采用了通过校验数据库版本号来实现sql的变更,这虽然能够解决大部分问题,但