一、介绍
在如今的开发过程只,内容变化已多单一的fragment,变成连续的,特别是以短视频或者直播为主的场景很多。从早起的Viewpage只能横向滑动,到如今的viewpage2可以支持横向或者竖向滑动。由于viewpage2的adapter在设计时支持缓存,导致想立马生效出现问题,不符合国内的业务场景。
二、viewpage2+FragmentStateAdapter设计原理分析
1.Viewpager2
Viewpaer2的设计和viewpage还是有区别的,最大的区别是viewpage是基础viewgroup,通过scroll控制整体view的滑动,在早起的时候,很多都是可以通过adapter去自定义缓存,但是viewpage2在androidx中新增的,是通过对recycleview进行二次封装出来的一个新业务。
从源码中可以看出,核心是recycleview,这个控件在之前v7包中是独立出来的,相对listview性能更好,在缓存和使用更流畅,也是支持横向或者竖向滑动。
2.FragmentStateAdapter
adpter的与Recycview.adapter还是有区别的。核心是adapter和holder。
2.1FragmentStateAdapter
继承recycView.adapter,里面对item进行了缓存,mFragments是一个key和fragment绑定的关系,下表就是fragment的索引。如果不经常对fragment移除,那么这个缓存可以大大提高性能。但是也就是这个原因,导致在设计的时候没考虑到移除立马生效等问题
2.2FragmentViewHolder
继承了RecycleView.holder,只要是在holder阶段,创建一个rootview->FragmentLayout将fragment包进来。提供容器
三、删除无法立即更新分析
通过第二段,了解了viewpage2+FragmentStateAdapter的设计,可以了解到这些设计的目的。但是我们在开发过程中的场景比较复杂,有人习惯了recycleView+recycleView.Adapter,以为viewpage2的核心也是这套,删除数据或者更新数据直接通过notify去处理,结果发现viewpage2移除不是我们要的那个索引,这是为什么呢?
1.问题分析:
这个问题和fragmentStateAdapter设计有关,在这个adapter中,mfragment的缓存是通过下表缓存的,也就是我们虽然把数据移除了,但是position在adapter的索引是连续的,还是从0开始,一直到最后一个元素,就算我们通知了notifyItemRemoved(position),但是数据移除了,下标也发生了变化,这时候我们通知移除的变成了当前位置后一个:position+1,和我们理想中还是有比较大的区别
这种做法和recycleView.Adapter内部不一样,很多开发人员遇到确实无法处理,想着从数据来处理,这种方法是行不通的。
四、解决方案
目前暴露的api还是很不好处理,网上的方案也是五花八门
1.重置adapter:
这种做法是删除数据,把viewpage2的adapter设为null,再用数据重新生成一个新的,这样做的弊端是影响了性能和体验
2.直接notify刷新:
发现下标索引乱了,数据移除失败
处理这个问题的核心是要把mfragment数组中的要对应的数据下表给移除,然后重新排序。只有保持索引下表和mfragment中的fragment对应,才能取到我们想要的view。
在adapter中也提供了一个remove的方法:removeFragment(position:Int),但是这个方法是私有的,我们只能通过反射来获取这个方法
步骤:
1.先将数据中索引下的数据移除
2.在移除removeFragment,最后在notifyItemRemoved刷新列表,保持索引的真实性
public fun remove(position: Int) {//先移除item在父类中的adapteradapter?.apply {val cls = this.javaClassval method = cls.superclass.getDeclaredMethod("removeFragment", Long::class.java)method.isAccessible = truemethod.invoke(this, position)}if (position == viewPager!!.currentItem) {viewPager?.setCurrentItem(position, false)}mlsit.removeAt(position)notifyItemRemoved(position)}
只有通过这样,才能保持下标与数据所在数组的准确性。