开启固定模式的viewpager的复用模式+PagerAdapter浅解析

来源:互联网 发布:mac 推出键 编辑:程序博客网 时间:2024/06/05 02:06

现在android的adapterview几乎都要用到viewholder模式,然而viewpager几乎被人遗忘使用viewholder。

其实ViewPager的ViewHolder模式也并不是那么的难使用,但adapter重写的方法不同,这让我们看似无从下手!

public class ImagePagerAdapter extends PagerAdapter{    public ImageShowAdapter(Context pContext){        this.mContext = pContext;    }    @Override    public void destroyItem(ViewGroup container, int position, Object object) {super.destoryItem(container,position,object)    }    @Override    public int getCount() {        return 0;    }    @Override    public Object instantiateItem(ViewGroup container,final int position) {        return super.instantiateItem(container,position);    }    @Override    public boolean isViewFromObject(View view, Object object) {        return super.isViewFromObject(view,object);    }}

分析一下:

getCount()方法我们很熟悉,就是获取item的个数的;

destoryItem()方法用来移除视图;

instantiateItem()方法添加指定视图;

isViewFromobject()方法判断要添加的视图是否和pager的一个view相关联。做如此判断的原因是instantiateItem()方法返回的是一个object对象而不是view对象。具体原因可以查看源代码中对应的方法调用情况。

分析完基本的方法作用,我们还要分析一下一些更深层次的东西。

打印过日志后,我们发现instantiateItem()方法在2个以上的数据的时候会被执行两次。第一次初始化position=0的视图,第二次初始化下一张position=1的视图。

向左滑到第二个视图页初始化position=3的视图。再向左滑先移除position=0即第一个视图页,再初始化第三个视图页(position=4)。当连续向后滑动的时候始终会重复这一过程,如果当前页是第三页,则在viewpager的容器内最多保留三个视图,即第二页和第四页,第一页则在初始化第四页之前被remover掉。

如果我们中途使用viewpager.setCurrentItemPosition()也是一样的,先初始化当前页,然后初始化左相邻,再初始化右相邻。viewpager保证它的容器container中最多有三个视图。结果向后滑动就是先加入下一张视图,再移除非近邻视图,向前滑动先加入视图再销毁视图。针对这种机制,我们应该如何做和viewholder结合做到视图的复用,答案从被销毁的视图说起。

如代码示,我们需要创建一个缓存池:

LinkedList<View> mCaches = new LinkedList<View>();

因为container最多保存三个视图,所以每当有加入视图使container的子view个数超过三个,必然会移除不需要的视图。这个被移除的视图会携带对应的数据且不会被立马被内存内回收,所以复用已存在的对象,大大减少了我们的开销。

@Overridepublic Object instantiateItem(ViewGroup container,final int position) {    View convertView =  null;    ViewHolder mHolder = null;    if(mCaches.size() == 0){        convertView = LayoutInflater.from(mContext).inflat(R.xml.item);        mHolder = new ViewHolder();        mHolder.imageView = (ImageView)convertView.findViewById(R.id.imageView);        convertView.setTag(mHolder);    }else{        convertView = (View)mCaches.removerFirst();    }    mHolder.imageView.setImageResource(mImages.get());    return convertView;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {    container.removeView((View)object);    mCaches.add((View)object);}private class ViewHolder{    ImageView imageView;}

不过从这里可以看出,如果当向前滑动的时候因为执行方法的前后顺序不一致,所以切换滑动方向的次数越多,缓存内存的view越多,而且在向后切换到向前滑动的时候,则不会产生复用。前一个问题很好解决,后一个问题暂时还未得到解决方案。修改destroyItem方法如下:

@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {    if(mCache.size() > 0){        mCache.clear();    }    container.removeView((View)object);    mCaches.add((View)object);}
到此,基本上缓存复用的代码已经全部完成。如果有更好的方案,欢迎留言讨论。另外附上一个非常酷炫轮播图库:

ViewPager轮播库

可以直接引用这个开源库中的ViewPagerEx,设置自己的adapter,达成简单的轮播图效果。也可以对控制器BaseSliderLayout进行修改,但是个人认为这种控制器的姿势略有错误,所以在内存开销上还是比较大,虽然达到了一定的开销时会自动回收。同时对于adapter的分析大体上也是和原生的ViewPager是一致的,只不过在初始化的时候会同时初始化左相邻,所以当数据为n的时候会按次序初始化0,n-1,1,另外当count为3的时候,每一次循环都看似没有需要销毁的地方。其实不是。这个库认为每一个循环都是独立的整体。所以不会认为当前需要被初始化的position(n-1)就是已经被初始化完成的position(n-1)。

eg:当前页是position1,已经初始化了position2,向前滑动,则还是先加入新的position2,再移除旧的position2。adapter始终忠实的执行着他的规则,不会因为size的改变而改变。

1 0
原创粉丝点击