美文网首页ViewPager
打造一个简单实用的安卓广告栏控件

打造一个简单实用的安卓广告栏控件

作者: leandom | 来源:发表于2017-02-25 12:45 被阅读945次

    思路

    循环 ViewPager 的两种实现方法这篇文章中介绍了广告栏的两种实现思路,但是直接用到项目中还是会有不少问题。

    • 方法1:将 count 设为无限大,制造一种假的循环
      这种方法在实际的项目中容易导致anr,在调用setCurrentItem或者在数据集发生改变时调用notifyDataSetChanged时有发生。
    • 方法2:在 ViewPager 的首尾添加一个重复的 View
      这种做法的问题是每循环一次会额外的多调用一次setCurrentItem,性能不佳,尤其是用户快速滚动时表现不够流畅。

    能否将两种方法结合起来呢,比如我将count设为200个,每次滑动到最后一页或者第一页的时候再执行setCurrentItem(middleItem)。当然,我还需要对滑出去的View做好回收,这点仿照ListView去做即可。

    说干就干。

    实现我们的PagerAdapter

    看码说话

    public abstract class CyclePagerAdapter extends PagerAdapter {
    
        private final int MAX_PAGES = 200;
        // 对View做缓存,防止每次都去inflate
        protected LinkedList<View> mScrapViews = new LinkedList<View>();
        // 最多缓存两个View
        protected int mMaxScrapViewSize = 2;
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View scrap = retrieveFromScrap();
            View view = getView(position, scrap, container);
            container.addView(view);
            return view;
        }
    
        private View retrieveFromScrap() {
            if (mScrapViews.size() > 0) {
                return mScrapViews.removeLast();
            }
            return null;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            View view = (View) object;
            container.removeView(view);
            if (mScrapViews.size() < mMaxScrapViewSize) {
                mScrapViews.add(view);
            }
        }
        // 返回 getRealCount 的整数倍,该数最大值为 MAX_PAGES,这里将MAX_PAGES设为200。
        @Override
        public int getCount() {
            if (getRealCount() < 2) {
                return getRealCount();
            }
            return getRealCount() * (MAX_PAGES / getRealCount());
        }
    
        public View getView(int position, View convertView, ViewGroup container) {
            int realPosition = getRealPosition(position);
            return getViewAtRealPosition(realPosition, convertView, container);
        }
    
        @Override
        public final boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    
        @Override
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }
    
        public int getRealPosition(int position) {
            if (getRealCount() == 0) {
                return 0;
            }
            return position % getRealCount();
        }
    
        public abstract int getRealCount();
    
        public abstract View getViewAtRealPosition(int position, View convertView, ViewGroup container);
    
    }
    

    在CyclePagerAdapter中,getCount返回值最大为200,并且该数是getRealCount的整数倍。这里我们还添加了一个回收机制,防止多次创建View导致性能损耗。

    使用时只需要继承CyclePagerAdapter即可。

    public class SimpleBannerAdapter extends CyclePagerAdapter {
    
            private static final int[] drawableIds = new int[]{R.drawable.desert, R.drawable.koala,
                    R.drawable.jellyfish, R.drawable.hydrangeas};
            private Context mContext;
    
            public SimpleBannerAdapter(Context context) {
                this.mContext = context;
            }
    
            @Override
            public int getRealCount() {
                return drawableIds.length;
            }
    
            @Override
            public View getViewAtRealPosition(final int position, View convertView, ViewGroup container) {
                if (convertView == null) {
                    convertView = LayoutInflater.from(mContext).inflate(R.layout.banner_item, container, false);
                }
                ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView);
                imageView.setImageResource(drawableIds[position]);
                return convertView;
            }
        }
    

    监听ViewPager滚动

    public class CycleViewPager extends ViewPager {
        private CyclePagerAdapter mCyclePagerAdapter;
        @Override
        public void setAdapter(PagerAdapter adapter) {
            super.setAdapter(adapter);
            if (adapter instanceof CyclePagerAdapter) {
                mCyclePagerAdapter = (CyclePagerAdapter) adapter;
                addOnPageChangeListener(mOnPageChangeListener);
                setMiddleItemInner(false, true);
            }
        }
        private OnPageChangeListener mOnPageChangeListener = new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float offset, int offsetPixels) {
                if (offset != 0) {
                    return;
                }
                if (mCyclePagerAdapter == null || mCyclePagerAdapter.getRealCount() <= 1) {
                    return;
                }
                // 第一页
                if (position == 0) {
                    setMiddleItemInner(false, false);
                    //最后一页
                } else if (position == mCyclePagerAdapter.getCount() - 1) {
                    setMiddleItemInner(false, false);
                }
            }
    
            @Override
            public void onPageSelected(int position) {
               
            }
    
        };
        // 设置到中间的item。当ViewPager滚动到第一页或者最后一页的时候调用。
        public void setMiddleItem() {
            setMiddleItemInner(true, true);
        }
    
        private void setMiddleItemInner(boolean setToFirstItem, boolean immediately) {
            if (mCyclePagerAdapter != null && mCyclePagerAdapter.getRealCount() > 1) {
                int currentItem = setToFirstItem ? 0 : getCurrentItem();
                final int middleItem = mCyclePagerAdapter.getCount() / 2 + mCyclePagerAdapter.getRealPosition(currentItem);
                if (immediately) {
                    setCurrentItem(middleItem, false);
                } else {
                    post(new Runnable() {
                        @Override
                        public void run() {
                            setCurrentItem(middleItem, false);
                        }
                    });
                }
            }
        }
    }
    

    至此,我们已经实现了一个可以循环滚动的ViewPager了,当然,自动滚动以及ViewPager指示器我们都还没有实现,如果想了解这部分可以参考我的github。我已经将这个项目上传到 https://github.com/leandom/CycleViewPager 这里了。

    相关文章

      网友评论

      本文标题:打造一个简单实用的安卓广告栏控件

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