RollPagerView源码解析

作者: Jacky_Y | 来源:发表于2017-08-02 16:41 被阅读128次

    非常感谢 Jude95 为我们分装的一个开源的轮播图控件。以下则跟着作者的思路阅读一下漂亮的源码~

    集成:

    RollPagerView

    最终你会看到这样的图:


    image.png
    • RollPagerView.java
    private HintViewDelegate mHintViewDelegate = new HintViewDelegate() {
            @Override
            public void setCurrentPosition(int position,HintView hintView) {
                if(hintView!=null)
                    hintView.setCurrent(position);
            }
    
            @Override
            public void initView(int length, int gravity,HintView hintView) {
                if (hintView!=null)
                hintView.initView(length,gravity);
            }
        };
    

    这个就是一个监听器,里边的hintView就是我们看到的 指示器(点 点 点),

    public void setHintViewDelegate(HintViewDelegate delegate){
            this.mHintViewDelegate = delegate;
    }
    
     /**
         * 支持自定义hintview
         * 只需new一个实现HintView的View传进来
         * 会自动将你的view添加到本View里面。重新设置LayoutParams。
         * @param hintview
     */
        public void setHintView(HintView hintview){
    
            if (mHintView != null) {
                removeView(mHintView);
            }
            this.mHintView = (View) hintview;
            if (hintview!=null&&hintview instanceof View){
                initHint(hintview);
            }
        }
    
    public interface HintView {
    
        void initView(int length, int gravity);
    
        void setCurrent(int current);
    }
    

    可以看出 HintView是一个接口,所以我们可以自定义hintview,并且可以在mHintViewDelegate中实现自定义操作。(mHintViewDelegate在除了初始化、自动播放的位置会调用,onPageSelected也会调用,所以hintview可以与viewpager同步),其余的就是一些自定义样式的设置和自动播放定时器的逻辑代码。

    接下来我们看下 StaticPagerAdapter.java -- 不可循环的viewpager

    public abstract class StaticPagerAdapter extends PagerAdapter {
        private ArrayList<View> mViewList = new ArrayList<>();
    
        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0==arg1;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
    
        @Override
        public void notifyDataSetChanged() {
            mViewList.clear();
            super.notifyDataSetChanged();
        }
    
        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View itemView = findViewByPosition(container,position);
            container.addView(itemView);
            onBind(itemView,position);
            return itemView;
        }
    
        private View findViewByPosition(ViewGroup container,int position){
            for (View view : mViewList) {
                if (((int)view.getTag()) == position&&view.getParent()==null){
                    return view;
                }
            }
            View view = getView(container,position);
            view.setTag(position);
            mViewList.add(view);
            return view;
        }
    
    
        public void onBind(View view,int position){
        }
    
        public abstract View getView(ViewGroup container, int position);
    
    }
    

    比较关键的方法是findViewByPosition,其实大体思路是这样的,默认情况下,viewpager 加载当前pager和相邻的pager, 所以每当有新的pager被加载时都会进入 instantiateItem方法,而 非当前pager和相邻pager,会被销毁,即进入destroyItem, instantiateItem有些类似ListView#getViewmViewList这里也是对view做了缓存机制,getView则是需要开发者自己去实现,加载什么样的view。

    之后,一起来看下LoopPagerAdapter.java -- 可循环的viewpager

    What? 咋循环?

    我们来看代码:

    public abstract class LoopPagerAdapter extends PagerAdapter{
        private RollPagerView mViewPager;
    
        private ArrayList<View> mViewList = new ArrayList<>();
    
        private class LoopHintViewDelegate implements RollPagerView.HintViewDelegate{
            @Override
            public void setCurrentPosition(int position, HintView hintView) {
                if (hintView!=null&&getRealCount()>0)
                    hintView.setCurrent(position%getRealCount());
            }
    
            @Override
            public void initView(int length, int gravity, HintView hintView) {
                if (hintView!=null)
                    hintView.initView(getRealCount(),gravity);
            }
        }
    
        @Override
        public void notifyDataSetChanged() {
            mViewList.clear();
            initPosition();
            super.notifyDataSetChanged();
        }
    
        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }
    
        @Override
        public void registerDataSetObserver(DataSetObserver observer) {
            super.registerDataSetObserver(observer);
            initPosition();
        }
    
        private void initPosition(){
            if (mViewPager.getViewPager().getCurrentItem() == 0&&getRealCount()>0){
                int half = Integer.MAX_VALUE/2;
                int start = half - half%getRealCount();
                setCurrent(start);
            }
        }
    
        private void setCurrent(int index){
            try {
                Field field = ViewPager.class.getDeclaredField("mCurItem");
                field.setAccessible(true);
                field.set(mViewPager.getViewPager(),index);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    
        public LoopPagerAdapter(RollPagerView viewPager){
            this.mViewPager = viewPager;
            viewPager.setHintViewDelegate(new LoopHintViewDelegate());
        }
    
        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0==arg1;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            int realPosition = position%getRealCount();
            View itemView = findViewByPosition(container,realPosition);
            container.addView(itemView);
            return itemView;
        }
    
    
        private View findViewByPosition(ViewGroup container,int position){
            for (View view : mViewList) {
                if (((int)view.getTag()) == position&&view.getParent()==null){
                    return view;
                }
            }
            View view = getView(container,position);
            view.setTag(position);
            mViewList.add(view);
            return view;
        }
    
        public abstract View getView(ViewGroup container, int position);
    
        @Deprecated
        @Override
        public final int getCount() {
            return getRealCount()<=0?getRealCount():Integer.MAX_VALUE;
        }
    
        public abstract int getRealCount();
    }
    

    这里只说与StaticPagerAdapter的区别,区别在哪呢,注意多了一个getRealCount方法,而getCount被重写,看吧,真正的count是Integer.MAX_VALUE,再看下 instantiateItem

    int realPosition = position%getRealCount();
    

    所以,真相大白了, getCountInteger.MAX_VALUE, 所以viewpager可以一直滑动,也就是说 instantiateItem会在一直滑动的过程中被不停的调用,记得之前说的mViewList吧,这里就起了作用,每个view都有个setTagrealPosition, 比如现在有3个有效的pager,当滑到第四个页面时, realCount为 3 % 3 = 0, 所以 mViewList里边tag为0的view就会被取出来复用, 这样,就既简单又完美地达到了viewpager 循环滚动~

    以上将RollPagerView的主要部分进行了分析,其余的细节就留给每个人自己吧,多看源码,多些感慨,多些成长,Cheer up!!

    此致。。敬礼。。

    相关文章

      网友评论

        本文标题:RollPagerView源码解析

        本文链接:https://www.haomeiwen.com/subject/safylxtx.html