真无限循环的ViewPager——解决两端滑动的平滑问题

来源:互联网 发布:中文域名续费价格 编辑:程序博客网 时间:2024/05/23 11:28

使用ViewPager实现图片轮播应该是大家很熟悉的做法。但是ViewPager有个缺点,不支持循环播放,滚到最右边不能继续右滚,同样,滚到最左边也不能继续左滚。这是个令人头疼的事情,好在程序员们神通广大,大家提出了两种方法解决这个问题。

1. 假无限循环。把PagerAdapter的getCount设为Integer.max,这样在一般情况下都不可能滚到边缘,达到无限循环的效果。但理论上它不是真的无限循环。

2. 真无限循环。假设有三张需要显示的图片ABC,额外在首尾添加两张辅助图片,形成序列CABCA。当向右滚动到最右的A时,立即跳转到左边的第二个图片A,当向左滚动到最左的C时,立即跳转到右边的倒数第二个图片C,这两次跳转使用语句setCurrentItem(1, false)和setCurrentItem(viewPager.getAdapter.getCount() - 2, false)。注意,由于第二个参数使用了false,因此跳转不会产生动画效果,因此这两次跳转对用户而言是不可见的,于是达到了无限循环的效果。这是真正的无限循环。

像我这样的强迫症患者基本都会选择真无限循环,为了避免理论上的一丝丝出错的可能。可不幸的是,真无限循环并没有想象中那么完美,在网友的大部分解决方案中,都存在这么一点点瑕疵,而这一点点瑕疵对于一名追求完美的程序员来说都是致命的。问题就出在从最后一个滚动到第一个的过程中。以上面图片序列CABCA为例,通常的解决方案如下图所示


当从C到A滚动时,我们会发现C的动画效果未完成就瞬间切到了A。对应这个图,其实是因为1的动画未完成就开始了2过程。而2过程是无动画的,所以1还没结束就直接切到了左数第二个图片A,出现了滚动效果不一致的情况。

之所以会这样,是因为我们把过程2的触发动作setCurrentItem(1, false)写到了onPageSelected方法中。直观上来看这样好像没什么问题,但是,经过我的测试,onPageSelected方法的调用并不表示切换已经完成,它只表示某个页面已经被选中,当手机离开屏幕时会调用这个事件。但是我们可以在任何位置离开屏幕,因此,在这个方法中调用setCurrentItem是不合适的。

我们的目标是1的动画效果全部完成再执行2,好在OnPageChangeListener中还有另一个回调onPageScrollStateChanged(int state)。这个方法在滚动状态改变时被调用,滚动状态共有三种:IDLE(空闲状态,没有任何滚动正在进行),DRAGGING(正在拖动图片),Settling(手指离开屏幕,自动完成剩余的动画效果)。详细讲解推荐博文OnPageChangeListener参数变化详细总结。我们可以等到到达IDLE状态后再执行setCurrentItem跳转,这样不就毫无痕迹了~

于是,实现代码非常简单

private final class MyPageChangeListener implements OnPageChangeListener {    private int currentPosition;    @Override    public void onPageScrollStateChanged(int state) {        if (state == ViewPager.SCROLL_STATE_IDLE) {            if (currentPosition == viewPager.getAdapter().getCount() - 1) {                viewPager.setCurrentItem(1, false);            }            else if (currentPosition == 0) {                viewPager.setCurrentItem(viewPager.getAdapter().getCount() - 2, false);            }        }    }    @Override    public void onPageScrolled(int scrolledPosition, float percent, int pixels) {        //empty    }    @Override    public void onPageSelected(int position) {        currentPosition = position;    }}
在onPageSelected方法中只需要记录新的位置索引,而跳转操作放到onPageScrollStateChanged中进行。好啦,大功告成!

经过测试,这个方案在非极端情况下表现的很好。所以,什么是极端情况?

当我们滑动频率非常高时(不排除有些无聊的人为了测试手机的流畅程度滑来滑去),该方案的跳转仍然存在瑕疵。就是在过程1进行中的所有滑动操作都得不到响应,导致1过程形成卡顿。这个问题仍待解决,但我相信会有解决方案的。不管怎样,当前的方案已经足够正常使用了,特别是在将其用于自动轮播的时候。

最后,感谢stackoverflow中的@tobi_b同学,他的方案给了我灵感,在此表示感谢。

2 0
原创粉丝点击