实现无限自动循环的Viewpager,广告轮询页面

第一次写博客,如果有什么问题,麻烦各位给个意见。

OK,进入正题,这次博客的内容是实现一个无限自动循环的ViewPager广告轮询控件。这几天公司要做一个商场的APP,刚好要用到这个功能,以前倒是写过自动轮询的ViewPager,而且github上也有不少这种开源控件,不过,我用的时候发现viewpager里面加载的ImageView只有二或三项的时候,自动向前滑是没问题,当自己手动往后滑的时候,突然就抛了个异常退出了,就像这样。


java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.,这个跟我们没有重写destoryItem()方法去移除控件,报的是一样的异常,然而我的destoryItem()里面是有移除掉当前view。这也是我打算写这篇博客的主要原因。下面我会提供一下解决方法。OK,接下来我们看代码。

下面是ViewPager的代码:

public class CircularViewpager extends ViewPager {
    private static final int START_CIRCULAR = 0;
    private static final int STOP_CIRCULAR = 1;

    private boolean flag = false;
    private int intervalTime = 3 * 1000;


    private OnPagerClickListener onPagerClickListener;
    private int allPagerCount;

    public void setOnPagerClickListener(int allPagerCount, OnPagerClickListener onPagerClickListener) {
        this.allPagerCount = allPagerCount;
        this.onPagerClickListener = onPagerClickListener;
    }

    /**
     * handler实现自动轮询
     */
    private Handler mhandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case START_CIRCULAR:
                    if (flag) {
                        setCurrentItem(getCurrentItem() + 1);
                        mhandler.sendEmptyMessageDelayed(START_CIRCULAR, intervalTime);
                    }
                    break;
                case STOP_CIRCULAR:
                    mhandler.removeCallbacksAndMessages(null);
                    break;
                default:
                    break;
            }
        }
    };

    public CircularViewpager(Context context) {
        this(context, null);
    }

    public CircularViewpager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * 开始自动轮询
     */
    public void start() {
        flag = true;
        mhandler.sendEmptyMessageDelayed(START_CIRCULAR, intervalTime);
    }

    /**
     * 停止自动轮询
     */
    public void stop() {
        mhandler.sendEmptyMessage(STOP_CIRCULAR);
        flag = false;
    }

    /**
     * 设置循环间隔时间
     */
    public void setIntervalTime(int intervalTime) {
        this.intervalTime = intervalTime;
    }

    private long downTime;

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downTime = System.currentTimeMillis();
                stop();
                break;
            case MotionEvent.ACTION_UP:
                long upTime = System.currentTimeMillis();
                if ((upTime - downTime) < 100) {
                    if (onPagerClickListener != null) {
                        onPagerClickListener.onPagerClick(getCurrentItem() % allPagerCount);
                    }
                }
                start();
                break;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 点击事件回调接口
     */
    public interface OnPagerClickListener {
        void onPagerClick(int pagerPosition);
    }

}
上面通过继承ViewPager来扩展其功能,用一个handler来实现自动轮询效果的,viewpager调用start()方法,往自身发送message,令设置viewpager当前item,即调用setCurrentItem()设置当前的item为当前item项加一。 如果要让viewpager停止自动轮询的话,就发送信息去调用handler的removeCallbacksAndMessages()方法把所有的回调跟信息移除。同时重写onTouchEvent()方法,实现用户手动滑动时,停止自动滑动。以及用户点击的监听事件(通过回调方法,反馈到viewpager里)。

接下来看一下Activitiy怎么使用这个ViewPager吧:

 imageViews = new ArrayList<>();
        int[] imgs = new int[]{R.drawable.a2, R.drawable.a3, R.drawable.a4, R.drawable.a5, R.drawable.a6};
        for (int img : imgs) {
            //新建ImageView,并添加到集合中
            ImageView imageView = new ImageView(this);
            imageView.setImageResource(img);
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageViews.add(imageView);
        }

        CircularPagerAdaper adapter = new CircularPagerAdaper(imageViews);
        viewpager.setAdapter(adapter);
        //开始自动循环
        viewpager.start();
        viewpager.setOnPagerClickListener(imageViews.size(), new CircularViewpager.OnPagerClickListener() {
            @Override
            public void onPagerClick(int pagerPosition) {
                Toast.makeText(MainActivity.this, "点击的是第" + pagerPosition + "项", Toast.LENGTH_SHORT).show();
            }
        });
其实用法跟普通的viewpager一模一样,只是多了一步viewpage.start()方法去开启自动轮询,以及多了个viewpager的点击监听方法。

接下来我们看一下adapter吧,我开头说的那个问题也是在这个地方解决的。

class CircularPagerAdaper extends PagerAdapter {

    private List<ImageView> list;

    public CircularPagerAdaper(List<ImageView> list) {
        this.list = list;
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

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

    //当list的数量小于等于三时,此处会报异常,此处把异常catch到不处理,然后destroyItem不移除
    @Override
    public Object instantiateItem(ViewGroup container, final int position) {
        try {
            container.addView(list.get(position % list.size()));
        } catch (Exception e) {
        }
        return list.get(position % list.size());
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (list.size() > 3) {
            container.removeView(list.get(position % list.size()));
        }
    }
}

这里对于开头那个问题我这里再重复一次吧,是这样的,viewpager里面默认的预加载项是一项,即当前Item的左右各预加载一项,一共要加载三个Imageview,当你通过setOffscreenPageLimit()方法来设置预加载项时,设置的值小于1的话,默认为1,即预加载项不能小于1,我们看一下源码吧。

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

public void setOffscreenPageLimit(int limit) {
        if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                    DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }
因为这个原因,当我往后滑动时,之前的view还未移除出去,就需要重新加载,所以就报错了。我的解决思路是这样的,我用一个try{}catch{}把报错的地方包裹起来,catch不进行处理,然后,当我加载的项小于等于三项时,就不进行removeView()。这样的就能能避免小于等于三项时Viewpager抛异常退出了。

OK,这个的实现思路就这样了,我的源码已经发布到github上了,上面添加了滑动时,有个小圆点随着ViewPager的滑动而切换的效果,各位想看的话也可以去下载,地址是:https://github.com/mnb65482/CircularViewpager

第一次写博客,有误的地方大家提个醒,多谢了!!!





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值