在很多项目中都有viewpager轮播效果的需求,网上很多轮播都是设置viewpager的count为int型最大值,虽然这种方式实现没有任何问题,但是我总是觉得不舒服,总在想是否能实现真正的轮播效果?
思路:
最初的想法,在OnPageChangeListener中判断:viewpager滑动到第一页的时候,如果向左滑动就直接把当前的item设置到最后一个;viewpager滑动到最后一页的时候,判断如果向右滑动就把当前item设置到第一个。这样子也确实是实现了功能,但是其中并没有viewpager切换中的动画效果,就是第一页和最后一页直接的切换显得特别生硬(我使用的切换方法是mViewPager.setCurrentItem(pageIndex,false),不使用动画切换,如果使用动画切换,他会从途径中间其他的页面)。
ok,其实这个时候功能已经实现了,但是对于用户友好性来说,还不达标。那么,如何解决滑动生硬的问题呢?
想了好久都没想出来,最后在网上搜索出来一个博客,忘记是哪个博客了!那位大牛的想法是:数据源的第一个前面,添加最后一个数据,最后一个后面,添加第一个数据,然后切换的时候,表面上看只切换一次,实际上在代码中是切换了两次的,下面看看核心代码。
/**
* 设置数据
* @param imgUrls
* @param flag 是否自动轮播
*/
public void setData(List<String> imgurls,boolean flag) {
this.imgUrls = imgurls;
this.flag = flag;
if(imgurls == null || imgurls.size()==0)
return;
/**
* 初始化的位置为1而不是0
*/
currentPosition = 1;
String lastUrls = imgurls.get(0);//显示的第一张图片地址
String firstUrls = imgurls.get(imgUrls.size()-1);//显示的最后一张图片的地址
/**
* 添加第一张图片到数据源中,这时候,这是最后一张图片
*/
imgUrls.add(lastUrls);</span><span style="font-size:24px;">
/**
* 添加最后一张图片的地址到数据源中,这时候,他是这个数据源中的第一个数据
*
imgUrls.add(0,firstUrls);
看上文中,如果我们本来的数据源中,有三张图片(分别是:1,2,3) ,那么经过我们特殊处理的之后,数据源中就应该有五张图片了(分别是:3,1,2,3,1),好了就是这样,其实在我最初的思想之上,我们需要解决的问题已经解决了,甚至于更简单了!懂否?
还是来解释一下吧:
最初到想法:
数据源是:1,2,3。那么,我只需要在1向左滑的时候切换到3,这个切换是没有办法直接在OnPageChangeListener的onPageSelected中直接判断的,因为viewpager是不支持从第一个直接滑动到最后一个的,要在ontouch中一起实现手势判断。这么做是真的很蛋疼。
得到大牛博客的想法之后是这样的:
数据源是:3,1,2,3,1。
我初始化viewpager的索引为1,因为这个时候我数据源中的第一张图片实际上是我想要展现在最后面的那张。第二张才是第一张图片。在整个切换过程主要解决的就是第一张切换到最后一张,和最后一张切换到第一张。
ps:这里的第一张和最后一张指的是显示效果中的第一张和最后一张,索引才是图片真正的位置所在
第一张切换到最后一张:
我从索引为1的图片向左滑动,切换到索引为0(实际图片是最后一张图片)的图片是可以直接切换的,在onPageSelected方法中判断出当前索引为0,我就直接使用mViewPager.setCurrentItem(pageIndex, false)方法设置到最后一张(在这里面也就是索引为3,实际展现出来的也是最后一张图片)。
最后一张切换到第一张:
索引为3的图片向右滑动,切换到数据源中最后一张(索引为4,实际显示的是第一张图片),这也是可以直接滑动切换的。
onPageSelected方法中判断出当前索引为4,直接使用mViewPager.setCurrentItem(pageIndex, false)方法设置到第一张(索引为1).
到此,整个循环滑动逻辑就ok了。当然,这样虽然实现了功能,但依旧会有一些问题,比如,当我们切换的时候,滑动较慢,手指仍然在屏幕上的时候,它依旧会很快就切换过去。这是一个小bug,使用一个布尔型的标记来锁定一个操作动作就可以很轻松的解决!
循环滑动已解决,轮播还会远吗?只需要定时切换ok了!
核心代码:
private class MyViewPagerChangeListener implements OnPageChangeListener{
private int pageIndex;
volatile boolean touchflag = false;
@Override
public void onPageSelected(int position) {
touchflag = false;
/**
* 计算当前实际位置开始
*/
pageIndex = position;
if(position == 0){<span style="white-space:pre"> </span>//第一个张的时候切换到倒数第二张(第一张和最后一张是一样的)
pageIndex = imgUrls.size()-2;
}else if(position == imgUrls.size()-1){<span style="white-space:pre"> </span>//最后一张的时候,切换到第二张(最后一张和第二张是一样的)
pageIndex = 1;
}
/**
* 计算当前实际位置结束
*/
//指示块设置
for (int i = 0; i < (imgUrls.size()-2); i++) {
ImageView view = (ImageView) indicatorLin.getChildAt(i);
if(i == (pageIndex-1)){ //这里减1得到真正的索引,这里pageIndex其实是真实位置而不是索引
view.setImageResource(R.drawable.banner_point_selected);
}else{
view.setImageResource(R.drawable.banner_point_unselected);
}
}
currentPosition = position;
if(position != pageIndex){
/**
* 保证动画执行完成
*/
touchflag = true;
return;
}
}
/**
* positionOffset : 翻页过程中的偏移量(增量) 范围:0.0 ---- 1.0(无限的接近1.0 最终变为0)
* positionOffsetPixels: 当前页面偏移的像素位置
*/
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
/**
*
* 当页面开始滑动的时候,三种状态的变化顺序为(1,2,0)
*
* arg0 ==1的时表示正在滑动
*
* arg0==2的时辰表示滑动完毕了(但未调用onPageSelected方法)
*
* arg0==0的表示什么都没做了,这代表一个动作的完成。
*
* onPageScrollStateChanged的1和2的状态都在onPageSelected 之前调用,只有0在onPageSelected之后调用
* @param arg0
*/
@Override
public void onPageScrollStateChanged(int status) {
if(status == 0){
if(touchflag){
touchflag = false;
mViewPager.setCurrentItem(pageIndex, false);
}
}
}
}
你晕了没有,我已经晕了!解释起来好麻烦。