• 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

  • 相关阅读:
    Java 数量为5的线程池同时运行5个窗口买票,每隔一秒钟卖一张票
    Android Notification
    Android DatePickerDialog TimePickerDialog
    Android Toast 提示按两次返回键退出
    Android Toast 自定义
    Android ProgressDialog 加载进度
    Android 自定义Dialog
    Android Dialog AlertDialog
    Android BaseAdapter ListView (明星简介列表)
    Android SimpleAdapter ListView (锁定手机,解锁手机的列表)
  • 原文地址:https://www.cnblogs.com/sishuiliuyun/p/14707994.html
Copyright © 2020-2023  润新知