PagerAdapter闪屏坑的修复
来源:互联网 发布:小爱和花儿知乎 编辑:程序博客网 时间:2024/06/06 01:41
详见 http://www.jianshu.com/p/29b708c62b33
背景
最近在填前同事的一个坑时,不小心遇到另外一个坑。 在一个礼物面板,原实现是gridView + ViewPager实现的(有几页礼物),在送用户免费礼物时,刷新ViewPager里面的item时,出现了闪屏。
其实很多童鞋知道,PagerAdapter在调用notifyDataSetChanged(), 如果使用默认的会不起作用
点进notifyDataSetChanged()
/** * This method should be called by the application if the data backing this adapter has changed * and associated views should update. */ public void notifyDataSetChanged() { synchronized (this) { if (mViewPagerObserver != null) { mViewPagerObserver.onChanged(); } } mObservable.notifyChanged(); }
可以看到
- mViewPagerObserver 是怎么传进来的呢?该类实际实现类是啥?
搜索全类只有一处赋值
void setViewPagerObserver(DataSetObserver observer) { synchronized (this) { mViewPagerObserver = observer; } }
可以看出是PagerObserver类,有ViewPager类初始化setAdapter(PagerAdapter adapter)的时候传过来。
回到刚才的 mViewPagerObserver.onChanged();PagerObserver的实现如下
@Override public void onChanged() { dataSetChanged(); }
恩,所以这里dataSetChanged()才是真正的实现:
void dataSetChanged() { // This method only gets called if our observer is attached, so mAdapter is non-null. final int adapterCount = mAdapter.getCount(); mExpectedAdapterCount = adapterCount; boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 && mItems.size() < adapterCount; int newCurrItem = mCurItem; boolean isUpdating = false; for (int i = 0; i < mItems.size(); i++) { final ItemInfo ii = mItems.get(i); final int newPos = mAdapter.getItemPosition(ii.object); if (newPos == PagerAdapter.POSITION_UNCHANGED) { continue; } if (newPos == PagerAdapter.POSITION_NONE) { mItems.remove(i); i--; if (!isUpdating) { mAdapter.startUpdate(this); isUpdating = true; } mAdapter.destroyItem(this, ii.position, ii.object); needPopulate = true; if (mCurItem == ii.position) { // Keep the current item in the valid range newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1)); needPopulate = true; } continue; } if (ii.position != newPos) { if (ii.position == mCurItem) { // Our current item changed position. Follow it. newCurrItem = newPos; } ii.position = newPos; needPopulate = true; } } if (isUpdating) { mAdapter.finishUpdate(this); } Collections.sort(mItems, COMPARATOR); if (needPopulate) { // Reset our known page widths; populate will recompute them. final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.isDecor) { lp.widthFactor = 0.f; } } setCurrentItemInternal(newCurrItem, false, true); requestLayout(); } }
这里的代码:
if (newPos == PagerAdapter.POSITION_UNCHANGED) { continue; } if (newPos == PagerAdapter.POSITION_NONE) { }
恩,明显是根据PagerAdapter.POSITION_NONE、PagerAdapter.POSITION_UNCHANGED来判断是否进行更新操作。 PagerAdapter.POSITION_UNCHANGED是什么时候打上标签的呢?
哎呀,getItemPosition方法返回的,于是有了解决方法1.
- mObservable.notifyChanged();
好吧这里是逐个通知Observer调用onChanged();/*** Invokes {@link DataSetObserver#onChanged} on each observer.* Called when the contents of the data set have changed. The recipient* will obtain the new contents the next time it queries the data set.*/public void notifyChanged() { synchronized(mObservers) { // since onChanged() is implemented by the app, it could do anything, including // removing itself from {@link mObservers} - and that could cause problems if // an iterator is used on the ArrayList {@link mObservers}. // to avoid such problems, just march thru the list in the reverse order. for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } }}
解决方案如下:
public int getItemPosition(Object object) { return PagerAdapter.POSITION_NONE; }
game over了么?当然没有。
上述解决方法只是解决了一个问题,注意测试的话,就会发觉引入了本文标题中提到的闪屏问题~~
到底是哪里出现的问题呢?前面的我们源码都读的没有问题,唯一没注意的就是最后更新的逻辑了。我们再次仔细看看:
注意标箭头的地方,原来这里是把整个item remove掉了,难怪会出现闪屏。 事实上我们也可以通过断点或打log的方式,看本文提到的gridView刷新时是否复用。
知道了这里,本文的解决方法如下,使用一个SparseArray来存储,然后手动刷新。
class MyPagerAdapter extends PagerAdapter { private MyGridViewAdapter mGridAdapter; private SparseArray<GridView> mViews = new SparseArray<>(); @Override public int getCount() { if (mInnerAdapter == null || mMaxRows == 0 || mColumns == 0) { return 0; } return (int) Math.ceil(mInnerAdapter.getCount() / (double) (mMaxRows * mColumns)); } // Remove a page for the given position. @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); mViews.remove(position); } // Determines whether a page View is associated with a specific key object as returned by instantiateItem(ViewGroup, int). @Override public boolean isViewFromObject(View view, Object object) { return view == object; } /** * PagerAdapter.POSITION_NONE 会导致调用notifyDataSetChanged * 调用 destroyItem 导致重新添加item,闪屏的出现 * 但是这里系统的实现bug, 见ViewPager$PagerObserver * 默认是POSITION_UNCHANGED 即不刷新, 调用notifyDataSetChanged无反应, * 这里使用手动刷新 * * @param object * @return */ @Override public int getItemPosition(Object object) { int index = -1; if (mViews != null) { index = mViews.indexOfValue((GridView) object); } return index != -1 ? index : PagerAdapter.POSITION_NONE; } @Override public void notifyDataSetChanged() { GridView view; int size = mViews.size(); for (int index = 0; index < size; index++) { view = mViews.valueAt(index); if (view != null) { ((MyGridViewAdapter) view.getAdapter()).notifyDataSetChanged(); } } super.notifyDataSetChanged(); } // Create the page for the given position. @Override public Object instantiateItem(ViewGroup container, int position) { GridView mGridView = new GridView(mContext); .... mGridAdapter = new MyGridViewAdapter(mInnerAdapter, position); mGridView.setAdapter(mGridAdapter); container.addView(mGridView); mViews.put(position, mGridView); return mGridView; } }
- PagerAdapter闪屏坑的修复
- Android PagerAdapter的用法
- PagerAdapter的几个方法
- 【PagerAdapter】Fragment的陷阱
- pagerAdapter的不同使用方法
- ViewPager的PagerAdapter.notifyDataSetChanged()
- PagerAdapter的使用方法
- PagerAdapter、FragmentPagerAdapter的使用
- pagerAdapter的简单介绍
- ViewPager+PagerAdapter的使用
- ViewPager的适配器PagerAdapter
- PagerAdapter
- PagerAdapter
- PagerAdapter
- PagerAdapter
- PagerAdapter
- ViewPager的PagerAdapter的介绍
- ViewPager的PagerAdapter的介绍
- 陈纪修老师《数学分析》 第10章:函数项级数 笔记
- hdu6035-树形dp-2017多校(2)&难-Colorful Tree
- 织梦DedeCms调用整站相关文章不限栏目的方法
- 两周的学习感触
- html心得
- PagerAdapter闪屏坑的修复
- Derek lying?
- jquery.twbsPagination.js动态页码分页demo
- CNTK API文档翻译(12)——CNTK进阶
- Quicksum
- CF758D---Ability To Convert(模拟+贪心思想(仅仅是思想))
- HTML:利用border写出三角形的三种方法
- C++进阶 降低文件间的编译依存关系(接口与实现解耦合)
- 【iOS】简单易用的折线图控件