ViewPager实现循环滑动

来源:互联网 发布:node.js实战 pdf 下载 编辑:程序博客网 时间:2024/05/16 04:42

android中用ViewPager实现循环滑动

ViewPager在android开发中是个非常常用的组件,最为大家所熟悉的应用场景大概是在app中自动滚动的广告了。当然了,还有很多其它的地方也会用它来实现。不过美中不足的是,它没提供相应的API来实现循环滑动,当滑到最后一页后就不能前进了,只能回退,而当退到第一页时也不能再回退了。可我们的需求中往往有些地方需要循环的效果,比如上面说的广告,一般都是在ViewPager中放几张广告图,然后循环地滚动着。这样就要我们想点别的办法来达到要求。

目前网上主要有两种方法来解决这个问题。

方法一:

一种是方式是重写adapter的getCount方法,返回一个很大的数,比如Integer.MAX_VALUE,然后在初始化时从Integer.MAX_VALUE的二分之一处开始显示,因为这个值很大,所以基本不可能滑动到position=0或者position=Integer.MAX_VALUE的位置,使用户感觉是在循环。

其主要代码像这样:

 private class MyAdapter extends PagerAdapter {    private List<View> listView;    public MyAdapter(List<View> listView) {        this.listView = listView;    }    @Override    public int getCount() {        return Integer.MAX_VALUE;    }    @Override    public boolean isViewFromObject(View view, Object object) {        return view == object;    }    @Override    public Object instantiateItem(ViewGroup container, int position) {        int index = position % listView.size();        int i = container.indexOfChild(listView.get(index));        if (i != -1) {            container.removeViewAt(i);        }        container.addView(listView.get(index));        return listView.get(index);    }    @Override    public void destroyItem(ViewGroup container, int position, Object object) {    }}

实际上这并不是真正的无限循环,如果只应用在手动滑的场景基本上不会出现滑到两端的情况,但如果是自动滑动的话这样做风险就比较大了。

方法二:

第二种方法是在原始数据的基础上增加一个头和尾,例如原始数据为[a1,b1,c1]三条数据,那么我们在头尾各增加一条数据变成[c2,a1,b1,c1,a2],c2与c1,a2与a1的内容是一样的,然后通过OnPageChangeListener中的onPageSelected方法判断viewpager是否滑到了头或滑到了尾,如到了头或尾就重新设置一下ViewPager的当前显示页,比如像上面的数据,当显示c2页的时候,就人为切换到c1,当显示a2的时候就人为切换到a1,这样无论往前往后滑都做到了真正的循环。

其主要代码是这样的:

@Overridepublic void onPageSelected(int position) {    if (position == 0) {        // 如果当前页是第1页,则跳到倒数第二页        setCurrentItem(mCount - 2, false);    } else if (position == mCount - 1) {        // 如果当前页是倒数第一页,则跳到第二页        setCurrentItem(1, false);    }}

不过这种方法有一点瑕疵,就是在头尾切换的时候会闪一下,也就是从a1滑向c2或c1滑向a2的时候,动画还没有完成,即c2或a2还没有显示完整的时候就切到c1或a1去了,给人很不友好的体验,于是就有了本文要介绍的这种方法。

完美解决方案:

本方法的实现原理跟第二种方法是一样的,就是在第二种方法的基础上解决了会闪的问题。会闪的原因是动画还没执行完就已经调用onPageSelected方法了,导致我们的人为切换过早执行了,所以看起来像闪了一下。一开始我在那里加了一个延时,就是等待动画完成再执行切换,但是这个延迟的时间很难掌握,经常出现原本调好的时间到下次执行时又会闪,延时太长了也不行,如果连续滑动的话会滑不过去,要等一下才行,所以这不是个长久之计。后来搜索一番才知道原来有地方可以精确地知道动画已完成,就是onPageScrollStateChanged这个方法里面,当它的参数state的值为SCROLL_STATE_IDLE 时,就代表动画已完成了。这样就好办了,我们可以在这里执行我们的头尾切换工作。

其主要代码像这样:

@Overridepublic void onPageSelected(int position) {    System.out.println("onPageSelected--->position = " + position);    mCurrentItem = position;}@Overridepublic void onPageScrollStateChanged(int state) {    switch (state) {        case SCROLL_STATE_IDLE:            // 已停止滑动            System.out.println("已停止滑动..." );            if (mCurrentItem == 0) {                setCurrentItem(mCount - 2, false);            } else if (mCurrentItem == mCount - 1) {                setCurrentItem(1, false);            }            break;        case SCROLL_STATE_DRAGGING:            // 正在手动拖动            System.out.println("正在手动拖动..." );            break;        case SCROLL_STATE_SETTLING:            // 正在归位,即松手后自动完成滑动的过程            System.out.println("正在归位..." );            break;    }}

效果图:

这样就完美的实现了循环滑动了。

最后,上源码:

demo源码:https://github.com/MingHuang1024/CyclicViewPager

番外篇

提个小问题:如果换一个场景,比如在日历中,我们每滑一下就显示下一个月或上一个月的日历,一般做个日历的话都要可以查询上百年的日期吧,如果使用上述方法,难道我们要传N多个view到ViewPagerAdapter里面吗?显然这是不可能的。如果只用三个view呢,让它们循环使用,这也有个问题,你怎么知道它滑到哪年哪月了呢?难道要自己用个计数器把它记起来?会不会太麻烦?下一篇来讲讲如何解决这个问题。请看大屏幕巧用ViewPager实现日历场景



由于水平有限,如果文中存在错误之处,请大家批评指正,欢迎大家一起来分享!

博客:http://blog.csdn.net/MingHuang2017

GitHub:https://github.com/MingHuang1024

Email:MingHuang1024@foxmail.com

微信:724360018

原创粉丝点击