ViewPager动态添加删除及刷新页面
文章目录
1. 前言
2. PagerAdapter 的刷新
2.1 源码解析
2.2 例子
3. FragmentPagerAdapter的刷新
3.1 源码解析
3.2 参考代码
1. 前言
在此之前,我总是不得其解,同样都提供了**notifyDataSetChanged()**方法,为什么 ListView 的adapter使用刷新的方法非常好用,而 ViewPager 的adapter使用刷新方法总是有这样那样的问题?百度了一下,查阅了很多篇文章,难以找到满意的解决方案。尤其涉及到ViewPager动态添加删除及刷新页面时,刷新成了难以克服的痛点。有的说ViewPager不能刷新,有的说需要重写这个方法,有的说需要重写那个方法。
为了搞清楚ViewPager的刷新问题,我参考多篇博客,又研读了源码,费了不少脑力。最终,这个问题还是被我搞定了。
2. PagerAdapter 的刷新
2.1 源码解析
要想真正的理解PagerAdapter的刷新,就一定要从源码找突破口。下面是PagerAdapter的类注释。
看PagerAdapter的最后一段,大概翻译一下,意思是:
PagerAdapter支持数据集改变。数据集的改变必须发生在主线程,并且以调用notifyDataSetChanged()方法结束,类似于AdapterView的适配器(继承自android.widget.BaseAdapter的)。数据集改变包括页面被添加、删除或位置改变。如果适配器实现了方法getItemPosition(Object),ViewPager将保持当前页面处于活动状态。
这段话有一个非常明确的信息:PagerAdapter可以通过调用notifyDataSetChanged()方法实现数据集的刷新。这就打破了有些人认为的ViewPager无法通过notifyDataSetChanged()刷新的认知。而且,这段话还告诉我们,我们可能需要实现方法getItemPosition(Object)。下面我面找到该方法的源码看一下:
需要看一下方法注释,大概意思是:
当主控件(个人认为是ViewPager)尝试确定item的位置是否已经改变时调用。如果给定的item位置没有改变,返回POSITION_UNCHANGED;如果item已经在适配器中不存在了,返回POSITION_NONE。
默认实现假设所有item永远不会改变位置并且总是返回POSITION_UNCHANGED(未改变)。
参数:object 代表一个item的对象(个人认为:View或Fragment),最初由instantiateItem(View, int) 方法返回。
返回 如果位置发生改变,返回对象的新位置,是0到getCount()的索引(不包括getCount());如果位置没有改变,返回 POSITION_UNCHANGED ;如果item不存在了,返回 POSITION_NONE 。
2.2 例子
注意:getItemPosition 的参数 object 与 adapter 的初始化方法:public Object instantiateItem(@NonNull ViewGroup container, int position) 的返回值有关,两者是对应关系。
其实这几个方法 isViewFromObject(view, object)、getItemPosition(object)、getItemPosition(object) 的 object 都与
instantiateItem(container, position) 的返回值一致。
可参考之后的文章
3. FragmentPagerAdapter的刷新
3.1 源码解析
由于FragmentPagerAdapter继承了PagerAdapter,那么是不是FragmentPagerAdapter只要重写了方法getItemPosition()就可以实现自己的刷新了?事实并非如此,即使我们重写了方法getItemPosition(),用FragmentPagerAdapter刷新界面时依然会有问题。查找博客,翻看源码,发现我们还需要重写一个FragmentPagerAdapter的方法getItemId()。下面是FragmentPagerAdapter的getItemId()方法的源码:
从方法描述我们可以看出:这个方法为给定position的item,返回一个唯一的id(identifier)。默认情况下,返回这个item的position。如果item的position可能发生改变,子类应该重写这个方法。
说的很清楚,如果ViewPager的item的position可能发生改变,子类应该重写这个方法。item的position发生改变的情况,恐怕只有添加,修改(互换位置),删除了。总之只要position可能发生改变,我们就需要重写这个方法。
所以,使用FragmentPagerAdapter除了需要重写其父类PagerAdapter的getItemPosition()方法,还需要重写getItemId()方法。
为什么FragmentPagerAdapter需要重写getItemId()方法呢?看一下FragmentPagerAdapter初始化item的方法:
此方法中使用到了getItemId()方法,用于方法makeFragmentName()生成Fragment的tag。FragmentMaganger会通过这个tag查找和添加Fragment。
我们都知道,ViewPager有预加载前一页和后一页的功能,预加载的页面(Fragment)会添加到FragmentPagerAdapter的FragmentManager中,其中的Fragment应当与它的tag一一对应,ViewPager切换到position页,就会把对应的Fragment attach到mCurTransaction中。在destroyItem()方法中会移除超出界限的Fragment。
当所有item的position不发生改变时,通过getItemId()可以获得对应的Fragment
当我们的position发生改变时,同一个position,上面代码中查找的Fragment将不再是原来的Fragment (tag改变了),刷新界面就会出现错乱或报错。
所以,只要我们需要保证,也就是一个position对应一个id,通过position能获得原来的Fragment。
3.2 参考代码:
还可以利用Fragment对象的hashCode,不用维护一个idList,前提是没有重写Fragment的hashCode。代码如下:
那么请问要怎么在fragment中实现增删改呢