现象
功能需求:横屏查看大图,左右滑动切换大图,大图上有一些涂鸦、描述文字信息,可以通过title上面的按钮隐藏某一张的涂鸦、描述信息;
点击title.hiddenDoodleBtn去改变javabean中的值,执行adapter.notifyDataSetChanged()去更新UI,此时就会出现当前展示也闪烁的问题;
分析*
重写getItemPosition()使notifyDataSetChanged()去执行
@Override
public int getItemPosition(@NonNull Object object) {
return POSITION_NONE;
}
使用POSITION_NONE
模式可以达到notifyDataSetChanged()去更新的效果,但是会导致,每个item都是remove了之后,再重新新建后再添加到布局上,这就会导致更新时候闪烁的根本原因;
分析ViewPager.dataSetChanged()方法可以分析出结果:
void dataSetChanged() {
………………
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); //删除item数据,导致闪烁根本原因
i--;
………………
continue;
}
………………
}
………………
}
解决
将instantiateItem()中得到的view保存在一个集合中,若需要更新的时候,直接从集合中得到item的View对象,用View直接去更新数据,就可以达到目的;
在使用这个过程中,原本是想做一个弱引用去保存View对象,避免View被回收抛nullException,但是用这种方法,无法更新;
错误代码:
public class ImgLabelPargeAdapter extends PagerAdapter {
………………
private List<WeakReference<SimpleImgLoadingLayout>> mImageViews = new ArrayList<>();
………………
@SuppressLint("CheckResult")
@Override
public Object instantiateItem(ViewGroup container, final int position) {
final SimpleImgLoadingLayout view = new SimpleImgLoadingLayout(mContext);
mImageViews.add(new WeakReference<>(view));
container.addView(view);
return view;
}
/**
* 必须要实现的方法
* 滑动切换的时销毁一个页面,ViewPager同时加载3个页面,假如此时你正在第二个页面,向左滑动,
* 将销毁第1个页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
mImageViews.remove(object);
}
}
正确逻辑
这里可以不用弱引用,集合里面的item数据不会一直增加,他跟你的设置有关ViewPager.setOffscreenPageLimit(3),集合里面的数据不会超过4个,会执行destroyItem()方法去释放item数据,所以直接使用强应用关系即可,此时就可以执行更新了;
public class ImgLabelPargeAdapter extends PagerAdapter {
private Context mContext;
private List<ImgLabelBean> lDatas;
private List<SimpleImgLoadingLayout> mImageViews = new ArrayList<>();
/**
* 设置是否显示标注信息
*
* @param position 当前itemView的索引
* @param showTag true:显示标签;false:隐藏标签;
*/
public void showTagOption(int position, boolean showTag) {
if (null == mImageViews || position >= mImageViews.size()) {
return;
}
SimpleImgLoadingLayout sll = mImageViews.get(position);
ImgLabelBean bean = lDatas.get(position);
if (null == sll || null == bean) {
return;
}
bean.setShowMark(showTag);
sll.showTagOption(showTag);
}
/**
* 简称/全称 切换
*
* @param position 当前itemView的索引
* @param showFullName true:全称;false:简称;
*/
public void showFullTagName(int position, boolean showFullName) {
if (null == mImageViews || position >= mImageViews.size()) {
return;
}
SimpleImgLoadingLayout sll = mImageViews.get(position);
ImgLabelBean bean = lDatas.get(position);
if (null == sll || null == bean) {
return;
}
bean.setShowFullName(showFullName);
sll.showFullTagName(showFullName);
}
/**
* 清空保存的Item View的数据
*/
public void clearView() {
if (null != mImageViews) {
mImageViews.clear();
}
}
public ImgLabelPargeAdapter(Context context, List<ImgLabelBean> lAllLabel) {
mContext = context;
this.lDatas = lAllLabel;
}
@Override
public int getCount() {
if (lDatas == null) {
return 0;
}
return lDatas.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public int getItemPosition(@NonNull Object object) {
return POSITION_NONE;
}
/**
* 必须要实现的方法
* 每次滑动的时实例化一个页面,ViewPager同时加载3个页面,假如此时你正在第二个页面,向左滑动,
* 将实例化第4个页面
**/
@SuppressLint("CheckResult")
@Override
public Object instantiateItem(ViewGroup container, final int position) {
//这里需要将每个position都要有占位操作,否则由于缓存的原因 mImageViews.size()会小于position大小
while (mImageViews.size() <= position) {
mImageViews.add(null);
}
final ImgLabelBean labelBean = lDatas.get(position);
final SimpleImgLoadingLayout view = new SimpleImgLoadingLayout(mContext);
view.loadImgUrl(labelBean.getImgUrl(), GlideLoadUtil.COMPRESS_TYPE_ORGINAL, GlideLoadUtil.ERROR_ONLY);
view.showTagOption(labelBean.isShowMark());
view.showFullTagName(labelBean.isShowFullName());
//避免图片还没有被imageView渲染出来就发送了数据的问题
Observable.timer(100, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
view.addMarkPoints(labelBean.getTagResults());
}
});
mImageViews.set(position, view);
container.addView(view);
return view;
}
/**
* 必须要实现的方法
* 滑动切换的时销毁一个页面,ViewPager同时加载3个页面,假如此时你正在第二个页面,向左滑动,
* 将销毁第1个页面
*/
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
mImageViews.set(position, null);
}
}
参考资料
Viewpager+FragmentStatePagerAdapter动态添加,删除,移动位置,(局部)更新页面(Fragment)不闪屏,不错位解决方案