developer.android.google.cn/jetpack
对navigation中action参数的destination,popUpTo,popUpToInclusive的一点笔记
destination顾名思义是导航的目的地,popUpTo(tag) 跳转到tag,并弹出tag之上的fragment,popUpToInclusive=true会弹出tag,false则不会弹出。
【Android进阶必学】JetPack指路明灯—Navigation
Fragment不用再每次切换是都重新创建了
jetpack系列之自定义FragmentNavigator
Android Navigation 如何动态的更换StartDestination &&保存Fragment状态
Google控件Navigation进阶使用
2.统一配置Animation
fun getObjectNacAction(id: Int, singleTop: Boolean = true, popid: Int? = null, clearPop: Boolean = false) =
NavActionBuilder().apply {
destinationId = id
navOptions {
if (popid != null) {
popUpTo(popid) {
inclusive = clearPop
}
}
launchSingleTop = singleTop
anim {
popEnter = R.anim.slide_left_in
popExit = R.anim.slide_left_out
enter = R.anim.push_left_in
exit = R.anim.push_right_out
}
}
}
开始使用 Navigation
要避免这种重复的话,可以在从C 到A 的action中指定 app:popUpTo 还有 app:popUpToInclusive 属性,如下:
<fragment
android:id="@+id/c"
android:name="com.example.myapplication.C"
android:label="fragment_c"
tools:layout="@layout/fragment_c">
<action
android:id="@+id/action_c_to_a"
app:destination="@id/a"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"/>
</fragment>
在到达了目的地 C 之后,回退栈中包含了目的地 A,B,C 的实
例.当导航回目的地 A 的时候,我们同时 popUpTo A ,这意味着
我们在导航的时候从回退栈中删除了 B,C. 伴随着
app:popUpToInclusive="true" 我们同时弹出了回退栈中的第
一个 A.要注意如果你没有使用 app:popUpToInclusive="true"
这个属性的话,你的回退栈中将会包含两个A 的实例.
两种跳转分别是传入nav_graph.xml当中的action id和resource id。
两种方法都可以实现跳转,但是我更建议用第一种,因为第一种可以配合着过渡的动画使用。
可以看到当我们倒C之后,后台堆栈当中包括A、B、C单个实例。当我们通过popUpTo A回到A的时候,意味着我们从堆栈当中把B和C删除了。当我们使用app:popUpToInclusive =“true”的时候,我们还会把A弹出堆栈并有效的清除它。如果我们没有使用app:popUpToInclusive =“true”那么也意味着我们的堆栈当中包含两个A的实例。
<action
android:id="@+id/action_c_to_a"
app:destination="@id/a"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"/>
Navigation.findNavController(it).navigate(R.id.twoFragment)
对于Button控件来说,还有另一种实现跳转的方法
view?.let { Navigation.findNavController(it).navigate(R.id.twoFragment)
问题即使以前的片段名为popUpTo的根,Android导航框架仍会显示后退箭头
使用Android导航框架时,我将IntroFragment作为根,将MainFragment作为IntroFragment的目标 . 在IntroFragment中我打电话:
view.findNavController().navigate(IntroFragmentDirections.actionIntroFragmentToMainFragment())
该操作的xml是:
<action android:id="@+id/action_introFragment_to_mainFragment" app:destination="@id/mainFragment"
app:exitAnim="@anim/slide_out_right" app:popUpTo="@+id/main" app:popUpToInclusive="true"/>
尽管有popUpToInclusive,但MainFragment工具栏中仍然会出现一个后退箭头 . 我一直无法找到摆脱它的方法 . 我已经确认按后面的软键确实会退出活动 .
Navigation问题详解——Fragment创建新的实例
···
这样就会出现一个问题,可能我们之前的OneFragment保存一些状态或者数据,当我们跳转OneFragment的时候,因为创建新的实例导致我们之前保存状态或者数据全部消失。
3.resource id替换为action id
看过我之前代码的同学,可能会注意到我特意强调
findNavController().navigate(R.id.action_oneFragment_to_twoFragment)
这里的id最好用action id,这一种可以配合着action里的动画使用,当然也可以配合着action里的属性popUpTo popUpToInclusive来使用。
之前有位同学在公共号给我留言,问下了Demo之后,为什么设置 popUpTo/popUpToInclusive不起作用,问题就在这里应该是action Id
问题依旧....
看似,我们在回退的时候,正常了。但是,我们的问题依旧存在,每次点击的时候依然会创建一个新的Fragment,一个新的实例,只是我们在创建新的实例的时候,把之前的给清除了而已。
我会在下一篇文章介绍如何解决这个问题!
···
Jetpack源码解析---Navigation为什么切换Fragment会重绘?
看到这里就很清楚了吧,Fragment的切换是通过replace方式
来切换的,并且加入回退栈,也就是说每次切换Fragment,都
会销毁视图和重新创建视图。至于为什么用这种方式我是真
的想不到,也没搞清楚初衷是什么?按照我们目前的开发来
说,Fragment的切换通常都会使用hide()、show(),而
replcae()的方式很少用,替换会把容器中的所有内容全都替
换掉,有一些app会使用这样的做法,保持只有一个fragment
在显示,减少了界面的层级关系。
不仅仅是这样,上篇文章有小伙伴问切换了Fragment之后,
点击返回按钮,发现之前的Fragment重走了onCreateView流
程,这就意味着之前的状态没了。对于这个问题其实根据上
面的分析,也能大概想到是因为什么,但是返回按钮的操作
我之前还真没有看过源码,所以这次顺便了解一下:
到这里就基本结束了,我只分析了一个大概,可以了解到点击返回按钮,同样也会重新创建视图,也就是onCreateView会重新走一遍。
.1 建议
这里我的建议是:如果你的每个Fragment
真的每次都需要重新绘制的话,你可以考虑使用Navigation
组件来实现,毕竟通过Navgation
组件真的很方便帮助我们切换导航,而且虽然布局会重新绘制,但是Google的官方Demo–SunFlower还是使用了这种方式,所以这里面我觉得:官方推荐我们使用Jetpack组件中的ViewModel、LiveData…等,可以发现SunFlowerdemo中,即便是切换Fragmengt也不会有很明显的卡顿现象,因为每个Fragment即便重新绘制,但是View所对应的ViewModel还在,数据并不需要重新加载或者请求,当然这仅仅是我自己的看法啊.
但是如果你没有这种场景的话,建议还是用普通的方式我们自己来控制切换吧,这样无论是基于Drawerlayout
还是BottomNaivgationView
的话,我们可以自己实现切换。这块我也不是很确定哈,也希望听取大家的意见和建议。
因为每个Fragment即便重新绘制,但是View所对应的ViewModel还在,数据并不需要重新加载或者请求,
Android-Jetpack笔记-Navigation之Fragment支持复用
Jetpack路由组件学习:深入理解功能强大的Navigation架构之接管系统的返回操作
deepLink
popUpTo属性表示堆栈返回到某个界面,其后的栈数据清空
//popUpToInclusive属性为true表示回到指定界面时,界面栈中是否还包括当前界面
//(如果栈中已经包含了指定要跳转的界面,那么只会保留一个,不指定则栈中会出现两个
//界面相同的Fragment数据)
a->b->c ,从c到a时,指定app:popUpTo="@id/listFragment"
,app:popUpToInclusive="true" 发现是新开了一个a,之前的a
走了,onDestroyView 和 onDestroy 方法,
如何 做到 从c到a时复用 a,也就是不销毁之前的那个,也不开启新的,直接复用老的fragment. 答案是使用popBackStack 方法,见下下图:情况2
情况1
a(ListFragment),b(DetailFragment),c(EditorFragment) 三个fragment分别在 onCreate,onCreateView,onViewCreated,onDestroyView,onDestroy方法中加入日志,其中 b 启动c的 action中加入了 app:popUpTo="@id/listFragment" ,app:popUpToInclusive="true"
使用修复过的NavHostFragment
image.png使用系统自带的NavHostFragment
image.png【Android进阶必学】JetPack指路明灯—Navigation
navigateUp
navigateUp与物理返回键的功能类似,即返回当前页面堆栈的栈顶页面,代码如下所示。
Navigation.findNavController(it).navigateUp()
当我们从A路由到B,B路由到C后,通过上面的代码,使用navigateUp返回,则路由返回路径为C到B,B到A,如果在A继续调用navigateUp,则不会响应,因为当前栈中只有唯一一个页面,而且是startDestination,所以不会再响应返回操作。
情况2
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是栈顶。
但如下图所示,界面并没有执行 a 的onDestroy 方法和
oncreate 方法 ,说明 a 并没有销毁
使用系统自带的NavHostFragment
inclusive为true时
image.png
inclusive为false时
image.png
使用修改过的NavHostFragment
inclusive为true时
image.pnginclusive为false时
image.png从验证结果看不管inclusive为false还是true, a 页面始终没有 被销毁。
使用修改过的NavHostFragment和系统自带的NavHostFragment我区别?
修改过的NavHostFragment 用action开页面时不会调用onDestroyView和onDestroy方法,而系统自带的会调用。
再考虑下面这样一个场景,A—B,B路由到C的时候,设置popUpTo="@id/A",如果popUpToInclusive=false,则跳转到C之后的路由栈为A—C,如果设置为true,则只剩下A在路由栈中,代码如下所示。
如上文字经过验证发现是错的,正确的结论是:
使用修改过的NavHostFragment,popUpToInclusive=true的情况
image.png使用修改过的NavHostFragment, popUpToInclusive=false的情况
image.png场景,A—B,B路由到C的时候,设置popUpTo="@id/A",如果popUpToInclusive=false,则跳转到C之后的路由栈为A—C,如果设置为true,则只剩下c在路由栈中,
网友评论