这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战
上篇文章介绍了Navigation的三大主要概念:NavHost、NavGraph 和 NavController,我们可以通过这些概念快捷地实现以下几个功能:
- 正确的导航以及回退
- 添加导航动画
- 模块化部分导航
- 方便的页面复用
- 从应用外跳转至指定页面
不知道以上功能有没有解决你开发的痛点呢,有的话就看下去吧。
从上篇文章的可视化导航图可以看到,NavGraph看起来像一个有向图,任何你在项目中可以走的路径,已经提前规定了,这对于项目开发是一种保障。实际上XML的NavGraph可视化是一种对其数据结构具象化的实现,XMl中所有的属性都可以是用代码实现的,这一点你应该提前知道。
跳转
跳转很简单,就是NavController执行某条导航路线,当你是这条导航的起点,它会将页面替换成终点。
nav_graph.xml
<fragment
android:id="@+id/test1Fragment"
android:name="com.example.navigationexample.ui.test.Test1Fragment"
android:label="Test1Fragment" >
<action
android:id="@+id/action_test1Fragment_to_test2Fragment"
app:destination="@id/test2Fragment" />
</fragment>
<fragment
android:id="@+id/test2Fragment"
android:name="com.example.navigationexample.ui.test.Test2Fragment"
android:label="Test2Fragment" />
Test.kt
findNavController().navigate(R.id.action_test1Fragment_to_test2Fragment)
回退
相关函数如下:
- popUpToInclusive()
- popUpTo()
- java/kt文件中用:NavController.navigateUp()或者NavController.popBackStack()来移除页面堆栈的栈顶页面
回退有三种实现,下面的将用示例来说明每种实现的逻辑。
默认回退
首页假设我们有多个页面(fragment):fA,fB,fC,fD。
nav.xml图中有fA->fB->fC->fD->fB这样一条路。当我们以默认方式走完这条路后,此时页面堆栈中有:A,B,C,D,B(自底到顶)。
当我们使用NavController.navigateUp()或者NavController.popBackStack()后,此时回退栈中就是:A,B,C,D
回退至特定页面(不覆盖目标特定页)
此时回退栈:A,B,C,D。当我们在fD->fB这个action中加上app:popUpTo后,如代码
<action
android:id="@+id/action_fd_to_fb"
app:destination="@id/fb"
app:popUpTo="@+id/fb"/>
再次走完这条路后,页面栈是这样:
A,B,B
回退至特定页面(覆盖目标特定页)
继续使用上面的示例:此时回退栈:A,B,C,D。我们再把app:popUpToInclusive="true"加上
<action
android:id="@+id/action_fd_to_fb"
app:destination="@id/fb"
app:popUpTo="@+id/fb"
app:popUpToInclusive="true"/>
页面栈就是这样:
A,B
这种方法与上种方法的区别是:旧的FragmentB会不会被删掉。
回退的限制
到了这个时候你可能有点疑惑了,我要是想回传数据从fD到fB怎么办?想用旧的FragmentB实例怎么办?
抱歉,官方原则上并不建议这样做,用Activity和Fragment保存数据和状态是不推荐的,会有数据丢失、状态丢失等隐患。所以在JetPack都会推荐viewmodel保存数据状态等数据。这样看来要不要旧的Fragment实例并没什么必要,在跳转时携带极少数据的情况以外(Navigation传参跳转),用viewmodel持有数据是最佳实践。这也解释了为什么默认情况下,从一个Fragment回退到前一个Fragment用的是replace而不是show了。