大学教学应用网站开发现状/讯展网站优化推广
ViewPager是Android应用开发中非常常用的一个控件,是一个可以让View左右翻页滑动的管理布局,需要和PagerAdapter配合使用,来创建每一页的View并显示。
ViewPager的使用其实是比较简单的,但是有一个比较重要的问题,就是ViewPager的数据刷新。PagerAdapter有一个notifyDataSetChanged()方法,根据Android官方文档的介绍
PagerAdapter支持数据的更改,但是数据的更改必须在主线程中进行,并且要调用notifyDataSetChanged()这个方法。我们知道所谓数据的更改,无非是页面的添加,删除和页面位置的变化。
按照官方文档的介绍,我们在改变PagerAdapter的数据源之后,然后调用一下notifyDataSetChanged()方法,应该就可以看到页面的变化,然而事情并没有那么简单,你会发现页面并没有任何的变化。那这个到底是什么情况。我们来看一下PagerAdapter的notifyDataSetChanged()这个方法都干了些什么
public void notifyDataSetChanged() {synchronized(this) {if (this.mViewPagerObserver != null) {this.mViewPagerObserver.onChanged();}}this.mObservable.notifyChanged();}
我们可以看到这里调用了this,mViewPagerObserver.onChanged(),这里实际上是观察者模式,去通知ViewPager数据发生了改变。我们再看下ViewPager的ViewPagerObserver 的onChanged ()方法又做了什么,
private class PagerObserver extends DataSetObserver {PagerObserver() {}public void onChanged() {ViewPager.this.dataSetChanged();}public void onInvalidated() {ViewPager.this.dataSetChanged();}}
ViewPagerObserver的onChanged()方法里调用了ViewPager的dataSetChanged()方法
void dataSetChanged() {int adapterCount = this.mAdapter.getCount();this.mExpectedAdapterCount = adapterCount;boolean needPopulate = this.mItems.size() < this.mOffscreenPageLimit * 2 + 1 && this.mItems.size() < adapterCount;int newCurrItem = this.mCurItem;boolean isUpdating = false;int childCount;for(childCount = 0; childCount < this.mItems.size(); ++childCount) {ViewPager.ItemInfo ii = (ViewPager.ItemInfo)this.mItems.get(childCount);int newPos = this.mAdapter.getItemPosition(ii.object);if (newPos != -1) {if (newPos == -2) {this.mItems.remove(childCount);--childCount;if (!isUpdating) {this.mAdapter.startUpdate(this);isUpdating = true;}this.mAdapter.destroyItem(this, ii.position, ii.object);needPopulate = true;if (this.mCurItem == ii.position) {newCurrItem = Math.max(0, Math.min(this.mCurItem, adapterCount - 1));needPopulate = true;}} else if (ii.position != newPos) {if (ii.position == this.mCurItem) {newCurrItem = newPos;}ii.position = newPos;needPopulate = true;}}}if (isUpdating) {this.mAdapter.finishUpdate(this);}Collections.sort(this.mItems, COMPARATOR);if (needPopulate) {childCount = this.getChildCount();for(int i = 0; i < childCount; ++i) {View child = this.getChildAt(i);ViewPager.LayoutParams lp = (ViewPager.LayoutParams)child.getLayoutParams();if (!lp.isDecor) {lp.widthFactor = 0.0F;}}this.setCurrentItemInternal(newCurrItem, false, true);this.requestLayout();}}
我们可以看到dataSetChanged()方法,中
int newPos = this.mAdapter.getItemPosition(ii.object);if (newPos != -1) { .......}
newPos不等于-1才会进入刷新数据的逻辑,newPos来自于PagerAapter的getItemPosition()方法,我们再看一下PagerAdapter的getItemPosition()方法。
public int getItemPosition(@NonNull Object object) {return -1;}
你会惊讶的发现,这特码的直接返回-1,调用PagerAdapter的notifyDataSetChanged()
ViewPager肯定不会有任何刷新啊。也是醉了。这算是Google工程师给的坑?。回到正题,那怎么办呢,我们应该需要重写PagerAdapter的getItemPosition()方法。PagerAdapter中实际上定义了两个常量。
public static final int POSITION_UNCHANGED = -1;public static final int POSITION_NONE = -2;
默认的返回的是POSITION_UNCHANGED,我们重写getItemPosition()方法返回POSITION_NONE就可以实现数据的刷新了。
public int getItemPosition(@NonNull Object object) {return POSITION_NONE ;}
但是这样做有个严重缺点就是,所有的getItemPosition()都返回POSITION_NONE的话,
每次都要移除所有的View,再重新添加到ViewPager中,这样效率太低。
有一个比较好的优化方案是:首先在instantiateItem()方法中给每个View设置一个Tag,就是View在数据列表也就是ViewPager中的索引位置,然后在getItemPosition()判断,当前View数据列表中是否有该View,索引位置有没有发生该变,如果当前数据列表没有该View int newsPos=mViewList.indexOf(object),也即newPos为-1,返回POSITION_NONE即可;如果当前数据列表中有该View,但是索引位置发生了改变也即 int oldPosition= (int) ((View) object).getTag();
newPosition!=oldPosition ,直接返回新的索引newPosition,如果位置没有发生改变
则返回POSITION_UNCHANGED。
@Overridepublic Object instantiateItem(@NonNull ViewGroup container, int position) {mViewList.get(position).setTag(position); //设置Tagcontainer.addView(mViewList.get(position));return mViewList.get(position);}
@Overridepublic int getItemPosition(@NonNull Object object) {int oldPosition= (int) ((View) object).getTag();int newsPos=mViewList.indexOf(object);if (newsPos>0&&newsPos!=oldPosition){((View) object).setTag(newsPos);return newsPos;}else{if (newsPos<0){return POSITION_NONE;}else{return POSITION_UNCHANGED;}}}
以上就是ViewPager中数据刷新的问题的分析及解决。