Android ViewPager的无限循环与自动滚动实现
前言
关于ViewPager时Android开发时经常要用到的控件,但系统提供的ViewPager往往总有一些功能限制,如不能无限循环,不能自动滚动,今天就介绍ViewPager无限循环和自动滚动的不同实现。
实现思路
实现方式有很多种,具体如下,后面会分析各种实现方式的优缺点。
无限循环方式
更改Adapter实现方式
即getCount返回Integer.MAX_VALUE
核心代码(这种方式其实是一种伪无限循环):
private MyPageAdapter extends PageAdapter {
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
处理数据源
即让ViewPager绑定的数据源模拟出一个循环效果即可,真实数据源唯0123,将其变为301230);
核心代码:
// 绑定到ViewPager的数据源
private List<T> datas = new ArrayList<>();
// 真实数据源
private List<T> realDatas = new ArrayList<>();
// handler
private Handler handler = new Handler();
// 要无限循环的ViewPager
private void initDatas() {
// 先加入真实数据的最后一个
datas.add(realDatas.get(realDatas.size() - 1));
for(T t : realDatas) {
datas.add(T);
}
// 最后加入真实数据第一个
datas.add(realDatas.get(0));
// 绑定数据的操作
...
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
@Override
public void onPageSelected(final int position) {
if(position == 0 || position == datas.size() -1) {
handler.postDelayed(new Runnable() {
@Override
public void run() {
viewPager.setCurrentItem(Math.abs(datas.size() - position -2), false);
}
})
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
从OnPageChangeListener入手
当滑动到临界值时,主动调用ViewPager的setCurrentItem方法;
具体实现思路如下(只是我的思路尚未代码验证):
// 数据源
private List<T> datas;
// 要无限循环的ViewPager
private ViewPager viewPager;
// 上次展示的页面
private int lastItem;
// 当前ViewPager展示页
private int currentItem;
// 是否正在向左滑
private boolean isDraggingToLeft = false;
// handler
private Handler handler = new Handler();
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset,
int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
lastItem = currentItem;
currentItem = position;
}
@Override
public void onPageScrollStateChanged(int state) {
switch (state) {
case ViewPager.SCROLL_STATE_DRAGGING:
break;
case ViewPager.SCROLL_STATE_SETTLING:
break;
case ViewPager.SCROLL_STATE_IDLE:
if (lastItem > currentItem) {
isDraggingToLeft = true;
}
if (isDraggingToLeft) {//左滑滑到0
if (currentItem == 0) {
currentItem = datas.size() - 1;
}
} else {//右滑滑到datas.size() -1
if (currentItem == datas.size() - 1) {
currentItem = 0;
}
}
// 展示一段时间,在通过ViewPager的setCurrentItem来设置到第0页
// sleep(bannerShowTime),这里只是模拟,可以用handler实现
handler.postDelayed(new Runnable() {
@Override
public void run() {
viewPager.setCurrentItem(currentItem);
}
}, 200);
break;
}
}
});
不足之处:
采用方法1时,当调用ViewPager的setCurrentItem方法时会造成ANR(只有在onCreate中调用不回出现),解决方法,可设置一个比较大的值,而不采用Integer.MAX_VALUE;
采用方法2和3时,
- 需要解决最后一个到第一个的闪屏问题,我的思路是采用handler来延时处理;
- 需要解决手动滑动时如何处理
自动滚动实现方式
自动滚动逻辑功能要求
- 手动滚动开始时,自动滚动暂停;
- 手动滚动结束时,自动滚动开始;
上面两个是通用处理,也就是说,不管哪种实现方式,都要考虑。
自动滚动实现方式
利用Handler来实现自动滚动
代码实现如下(核心代码如下):
// 要自动滚动的ViewPager private ViewPager viewPager; // 定义是否开启自动滚动,默认开启 private boolean isAutoPlay = true; // 默认自动滚动任务延时两秒执行 private int delayTime = 2000; // 定义处理自动滚动的handler private Handler handler = new Handler(); // 当前ViewPager展示页 private int currentItem; /** * 开启自动滚动的方法 */ private void startAutoPlay() { isAutoPlay = true; handler.removeCallbacks(task); handler.postDelayed(task, delayTime) } /** * 暂停自动滚动 */ public pauseAutoPlay() { isAutoPlay = false; handler.removeCallbacks(task); } /** * 自动滚动核心代码 */ private final Runnable task = new Runnable() { @Override public void run() { if (isAutoPlay) { // 自动滚动逻辑处理 currentItem = viewPager.getCurrentItem(); currentItem++; if(currentItem == randyPagerAdapter.getCount() -1) { currentItem = 0; viewPager.setCurrentItem(currentItem); handler.postDelayed(this, delayTime); } else { viewPager.setCurrentItem(currentItem); handler.postDelayed(this, delayTime); } } else { handler.postDelayed(this, delayTime); } } };
利用定时任务(Timer和TimerTask)来实现
代码实现(核心代码如下):
private static final int DISPLAY_TIME = 3000; private Handler hander = new Handler(); private Timer timer = new Timer(); private TimerTask timerTask; private ViewPager mViewPager; private Runnable runable = new Runnable() { @Override public void run() { //true表示平滑滚动 mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1, true); } }; public void startScroll() { timerTask = new TimerTask() { @Override public void run() { hander.post(runable); } }; timer.schedule(timerTask, DISPLAY_TIME, DISPLAY_TIME); } public void stopScroll() { timerTask.cancel(); timerTask = null; }
总结
- 在处理无限循环时,核心思路就是对临界值的处理,当然也可以通过方法1来绕过这种处理,但要注意一点就是怎么设置getCount的返回值以达到最优的性能;
- 在处理自动滚动时,无非就是通过handler的postDelayed方法的特性以及Android定义的定时器进行处理。