【Android进阶必学】JetPack指路明灯—Navigation(1)

对于navigation标签来说,最重要的是它的startDestination属性,即类似MainActivity的概念,代表了路由的起点。多个destination连接起来就组成了一个栈导航图,destination之间连接就是action。

每个fragment标签,代表了一层路由,当然,这里不仅仅可以是fragment,也可以是Activity、Dialog。

在每个fragment标签里面的action标签,就代表路由的具体行为,destination就是该路由的终点。

创建Activity并引入NavHostFragment


在Activity的xml布局中,通过FragmentContainerView来创建这些Fragment的容器,代码如下所示。

FragmentContainerView是一个特殊的Fragment,只能添加Fragment,

  • app:navGraph:这里需要指定前面在res文件夹下创建的navigation文件

  • app:defaultNavHost=“true”:代表可以拦截系统的返回键,用来托管路由

  • android:name=“androidx.navigation.fragment.NavHostFragment”:代表这个容器就是用来管理Fragment的容器

FragmentContainerView内部会通过反射的方式,初始化名为name所指定的class——NavHostFragment,它就是所有需要管理的Fragment的Container。

在NavHostFragment中,有两个重要的参数,即mGraphId和mDefaultNavHost,保存着我们从xml中解析出来的数据。同时,在onCreate的时候,创建了NavController,与mGraphId进行绑定。

使用路由


在Fragment中,可以通过NavController来进行路由,代码如下所示。

class LoginFragment : Fragment(R.layout.fragment_login) {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

login.setOnClickListener {

Navigation.findNavController(it).navigate(R.id.action_loginFragment_to_registerFragment)

}

}

}

同时,也可以通过Bundle来进行参数的传递,这跟之前使用Fragment基本类似,代码如下。

Navigation.findNavController(it).navigate(R.id.action_registerFragment_to_mainListFragment, bundleOf(“name” to “xuyisheng”))

所以这里可以很方便的进行路由选择,针对不同的判断条件,选择不同的路由action。

为什么能获取

这里有个地方很有意思,那就是为什么通过view可以获取NavController。

Navigation.findNavController(View)

从源码中可以发现。

实际上,他是从Tag中取出的,而这个Tag,则是在NavHostFragment的onViewCreated中创建的。

这样的API设计,可以让用户传入View后进行遍历,通过查找指定Tag来获取NavController,简化了调用方式。

路由跳转


通过NavController进行路由跳转,有多种方式,比如通过路由action指定,也可以指定跳转的destination。

action

这就是前面提到的路由方式,也是最常用的路由方式,代码如下所示。

Navigation.findNavController(it).navigate(R.id.action_loginFragment_to_registerFragment)

不过要注意的是,使用action进行路由跳转,要保证当前页面的实例是存在的,否则会抛出异常。

destination

直接使用destination的id,同样可以跳转到指定的destination,代码如下所示。

Navigation.findNavController(it).navigate(R.id.mainListFragment)

这种方式,同样是创建一个新的页面实例。

返回控制


路由的返回控制,有两种方式,navigateUp和popBackStack。下面通过一个例子来演示下,如何对路由进行返回控制,下面有三个Fragment,A-B-C。

navigateUp

navigateUp与物理返回键的功能类似,即返回当前页面堆栈的栈顶页面,代码如下所示。

Navigation.findNavController(it).navigateUp()

当我们从A路由到B,B路由到C后,通过上面的代码,使用navigateUp返回,则路由返回路径为C到B,B到A,如果在A继续调用navigateUp,则不会响应,因为当前栈中只有唯一一个页面,而且是startDestination,所以不会再响应返回操作。

popBackStack

navigateUp只能响应向上一级的路由控制,而不能跨级进行路由返回,popBackStack则是对其的补充,可以指定路由返回的action,代码如下所示。

Navigation.findNavController(it).popBackStack(R.id.loginFragment, true)

当我们从A路由到B,B路由到C后,通过popBackStack返回,指定要返回到的Fragment的id,即可直接返回到指定位置,第二个参数inclusive,代表返回操作是否包含指定的Fragment id。

这里要注意的是,当你指定返回到A,同时inclusive为true的时候,A也是不会被移除的,因为A是栈顶。

实际上,navigateUp内部就是通过popBackStack实现的。

借助popBackStack的返回值,可以在跳转失败时,创建新的Fragment。

val flag = Navigation.findNavController(getView()).popBackStack(R.id.someFragment, false)

if (!flag){

Navigation.findNavController(getView()).navigate(R.id.someFragment)

}

defaultNavHost

app:defaultNavHost="true"这个属性是我们最早在FragmentContainerView中设置的,通过这个属性,可以让当前的NavHostFragment拦截系统的返回键,也就是说,只要当前Fragment堆栈中有元素,就拦截系统返回键,用于Fragment堆栈的出栈,直到堆栈中只剩下一个元素,则将系统返回值的功能交还给Activity。

popupTo

当我们通过navigation去进行路由的时候,每次都会创建一个新的实例,所以,当navigation出现下面的循环图时,如下所示。

这样的循环图,会导致页面路由变成这样A—B—C—A—B—C,这就导致页面栈中存在了大量重复的页面。

所以在这种场景下,就需要在A—B—C之后,在C—A的路由中,配置popUpTo=“@id/A”,同时设置popUpToInclusive=true,将旧的A界面也移除,这样,C—A路由之后,页面栈中就只剩下A了(如果是false,则会存在两个A的实例),代码如下所示。

<fragment

android:id=“@+id/mainListFragment”

android:name=“com.example.navigation.MainListFragment”

android:label=“MainList”>

<action

android:id=“@+id/action_mainListFragment_to_loginFragment”

app:destination=“@id/loginFragment”

app:popUpTo=“@id/loginFragment”

app:popUpToInclusive=“true” />

再考虑下面这样一个场景,A—B,B路由到C的时候,设置popUpTo=“@id/A”,如果popUpToInclusive=false,则跳转到C之后的路由栈为A—C,如果设置为true,则只剩下A在路由栈中,代码如下所示。

<fragment

android:id=“@+id/registerFragment”

android:name=“com.example.navigation.RegisterFragment”

android:label=“Register”>

<action

android:id=“@+id/action_registerFragment_to_mainListFragment”

app:destination=“@id/mainListFragment”

app:popUpTo=“@id/loginFragment”

app:popUpToInclusive=“true” />

这个场景可以使用于登录注册之后跳转主页的场景,当跳转主页后,就应该把登录和注册的界面pop出栈。

所以,从上面的实例就可以分析出,在action中配置popUpTo属性,指的是在当前路由中,一直将页面出栈,直到指定的页面为止,而popUpToInclusive,则是代表包含关系,是否包含指定的页面。

个人感觉这个API命名为popUntil可能更合适一点。

在代码中,也存在类似的调用方法。

NavOptions.Builder()

.setPopUpTo(R.id.fragmentOne, true)

.build()

Navigation动态加载


除了在xml中设置navGraph,有很多场景下,我们会根据业务场景动态设置一些navGraph,或者某些navGraph是需要动态获取一些参数之后才去初始化的,这时候,就可以使用Navigation的动态加载方案。

首先,需要在Fragment容器中,去掉navGraph的引用,然后在Activity中,动态指定要引用的navGraph,代码如下所示。

// 动态加载

val navHostFragment = supportFragmentManager.findFragmentById(R.id.navFragmentHost) as NavHostFragment??:return

val navigation = navHostFragment.navController.navInflater.inflate(R.navigation.nav_graph_base)

navigation.startDestination = R.id.loginFragment

navHostFragment.navController.graph = navigation

实际上和动态Inflate布局再添加布局到容器的场景非常类似,Navigation动态加载也是将navGraph从xml中创建好之后设置给navigation,接收参数的话,与正常的参数传递是一样的。

添加路由动画


路由切换动画是action的属性,当我们使用action进行路由时,可以指定目标Page,和原Page的动画切换效果,它包含下面几个属性。

  • enterAnim:目标Page进入动画

  • exitAnim:目标Page进入时,原Page退出动画

  • popEnterAnim:目标Page退出动画

  • popExitAnim:目标Page退出时,原Page退出动画

有点绕,但是这个和原来Activity间使用的overridePendingTransition是一样的。这里的动画,可以通过在Design界面中,直接选中action来设置,也可以直接在代码中指定。设置好后,代码如下所示。

如果你进阶的路上缺乏方向,可以加入我们的圈子和安卓开发者们一起学习交流!

  • Android进阶学习全套手册

    img

  • Android对标阿里P7学习视频

    img

  • BATJ大厂Android高频面试题

    img

最后,借用我最喜欢的乔布斯语录,作为本文的结尾:

人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。
你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。
最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
中…(img-jbXHUZiQ-1715023211901)]

  • Android对标阿里P7学习视频

    [外链图片转存中…(img-ba1Ogj5F-1715023211904)]

  • BATJ大厂Android高频面试题

    [外链图片转存中…(img-wyOhzNE3-1715023211908)]

最后,借用我最喜欢的乔布斯语录,作为本文的结尾:

人这一辈子没法做太多的事情,所以每一件都要做得精彩绝伦。
你的时间有限,所以不要为别人而活。不要被教条所限,不要活在别人的观念里。不要让别人的意见左右自己内心的声音。
最重要的是,勇敢的去追随自己的心灵和直觉,只有自己的心灵和直觉才知道你自己的真实想法,其他一切都是次要。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 18
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值