ViewPager(二) Adapter的爱恨情仇

上一篇 ViewPager(一) 初相识 中,我们认识了 ViewPager ,也学习了他在子 View 是 Fragment 的情况下的用法,还在最后留了一个小尾巴,我们只是描述了作为 ViewPager 得力助手 PagerAdapter 的两个谷歌原生亲儿子的区别,但是既没有用代码体现,也没有从源码分析,这一篇,我们就来解决这个问题,不过在此之前,我们还需要了解点别的东西。

为什么绕不开基类PagerAdapter

Adapter到底有什么爱恨情仇呢?因为你想要学会ViewPager的时候,只了解两个FragmentPagerAdapter远远不够的,但是在上一篇我们却直接绕过他们的父亲,是为了让读者更容易上手,因为Fragment确实是很常用的场景,所以先以专门负责Fragment加载的两个子Adapter开篇。

言归正传,在开始剖析两个亲儿子 FragmentPagerAdapter 和FragmentStatePagerAdapter 之前,又不能不介绍它们的父亲。虽然子类隐藏了父类的某些细节,暴露给使用者更简单的接口,从而方便使用。但是不代表我们不应该弄清楚这背后的原理。作为开发者,只有这样才能再使用的时候更加的如鱼得水。更何况,在开发中并不是 ViewPager 的子 View 都是 Fragment,还有可能是其他元素,其实只要是 View 就行。比如说,可以翻动的 ImageView。所以这个时候直接实现 PagerAdapter 就显得尤为重要,所以本篇笔者主要用来介绍基础的Adapter的用法,两个亲儿子我们再往后放放,请各位看官见谅。

如果已经知道 PagerAdapter 的基本用法及原理,只想弄明白 FragmentPagerAdapter 和 FragmentStatePagerAdapter 的区别,

请移步:ViewPager(三) 两个熊孩子天生不一样

这次,我们先上代码

与上一篇代码基本相似,所以我只贴有区别的地方,主要是Adapter的实现

//用于保管传递给ViewPager的子View的集合
private List<ImageView> mImageList;
//管理子View中,加载图片资源的数组
private int[] mPicIds = new int[]{R.mipmap.cat, R.mipmap.monkey, R.mipmap.sun, R.mipmap.thanks};

//在InitView方法中我们完成集合填充
private void initView() {

        mPager = findViewById(R.id.pager);
        mImageList = new ArrayList<>();

        for (int i = 0; i < mPicIds.length; i++) {
        	//看的出来,我们给ViewPager传递的子View 是ImageView类型
            ImageView image = new ImageView(getApplicationContext());
            image.setScaleType(ImageView.ScaleType.FIT_CENTER);
            mImageList.add(image);
        }
 }

然后是Adapter的实现, initAdapter() 和之前的一样,这里省略,但是一定要通过setAdapter(PagerAdapter adapter) 方法把适配器传给 ViewPager,否则子View将无法显示。


class MyBaseAdapter extends PagerAdapter{

        @Override
        public int getCount() {
            return mImageList.size();
        }

        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
            return view == object;
        }

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            ImageView image = mImageList.get(position);
            container.addView(image);
            //给ImageView里填充Drawable,这步也可以放在initView中,添加到mImageList之前设置
            image.setImageDrawable(getResources().getDrawable(mPicIds[position]));
            return image;
        }

        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        //移除并回收
            container.removeView((View) object);
            object = null;
        }
    }

从上边的例子,可以看出,我们在使用适配器配合 ViewPager 工作的时候,抽象类需要我们实现四个方法,来完成view的装载和卸载。

PagerAdapter 的使用讲解

int getCount();//所有的Adapter都需要实现的方法,返回要显示的数量
boolean isViewFromObject(@NonNull View view, @NonNull Object object);
//这个方法和getCount()的都是抽象方法,必须实现。主要用来确认保存的view是否和通过instantiateItem (下边的方法)返回的Object相同,这个方法是为了保证加载正确性的。
//这个方法虽然不是抽象方法,但是也是必须实现的,
Object instantiateItem(@NonNull ViewGroup container, int position);

因为ViewPager会通过Adapter调用的,如果不实现的话,会抛异常,不信我们看PagerAdapter的源码:

public Object instantiateItem(@NonNull View container, int position) {
    throw new UnsupportedOperationException(
            "Required method instantiateItem was not overridden");
}

不知道谷歌为什么不把这个设置成抽象方法,你不实现就抛异常,然后再提示你去实现。
这个方法非常重要,因为ViewPager加载的原始View来源都是通过Adapter的这个方法,传递给ViewPager的。你把你想要展示的View通过这个方法返回就可以了。

//这个方法,顾名思义,就是销毁子view用的,这个方法和上一个方法一样,虽然没有抽象必须实现,但是你不实现,就会给你抛异常,提示让你去实现,
void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object);

源码如下:


public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    destroyItem((View) container, position, object);
}

@Deprecated
public void destroyItem(@NonNull View container, int position, @NonNull Object object) {
    throw new UnsupportedOperationException("Required method destroyItem was not overridden");

内部调用一个过期的重载方法,然后抛异常。这个方法是让我们做内存的管理的。这个方法是在 ViewPager 需要清理不显示的View的时候调用的,他把具体实现交给开发者。 一般情况下我们可以调用 container.removeView((View) object); 从容器中移除,然后 object = null; 将对象置空,便于垃圾回收。
当然你也可以有自己的管理策略,实现更加个性化的方案。

总结下,在注册 Adapter 和 draw 绘制等时机,会调用 getCount() 确定需要显示的View的数量,然后在真正 addView() 加载子View的时候会调用instantiateItem()获取具体要显示的View,然后在装载显示的时候又会调用 isViewFromObject() 方法再次确认类型正确,然后在 ViewPager 不需要某个View的时候,会调用 destroyItem() 方法来请求开发者去销毁。
重载以上四个方法,就能实现ViewPager的子View为任意类型的滑动浏览功能,是不是感觉瞬间可以把ViewPager玩的飞起呀。

哈哈,小有成就就好,跟着笔者继续探究上一篇的尾巴,来看看FragmentPagerAdapter 和 FragmentStatePagerAdapter 为什么不同呢
请看下一篇:ViewPager(三) 两个熊孩子天生不一样
————————————————
版权声明:本文为CSDN博主「郝振兴」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_39095733/article/details/84207223

原文地址:https://www.cnblogs.com/sishuiliuyun/p/14707994.html