好久也没写过博客了,今天兴致大发突然想写一篇博客,哈哈哈哈.....
今天的博客是关于无限轮播的广告,无限轮播的广告在各个市场应用是相当常见的,可能平时我们自己做的东西中是没有无限播放的。只是单次播放完之后直接跳到了第一页,当用户想从最后一张向右滑动的时候,却发现滑不动了,这是种不太好的体验.....
废话不多说了,先上个动图看看
1.实现方式的原理
其实就是在数据集中增加了两张图片,在首位置增加最后一个图片,在最后一个位置增加第一张图片,然后处理滑动边界的时候,通过滑动边界的一些处理做到了伪无限循环。比如你从倒数第二张图片向滑动的时候,此时右边显示的图片是第一张图片,待你滑动完成时,也就是最后一张图片完全显示出来的时候,viewpager的位置立即切换到第二项,现在这时候显示出来的是第一张图片,指示器的位置也切换到第一个位置。这样就重新进行下一轮的循环啦。从第一张图片往左划的原理也是一样的。接下来从代码上讲讲一些道理。
2.重写的viewpager中滑动边界处理的代码
OnPageChangeListener mOnPagerChangeListener = new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (null != getAdapter() && getAdapter().getCount() > 1) {
int count = getAdapter().getCount();
if (position == 1 && prePosition == count - 1) {
prePosition = position;
return;
}else if (position == count - 2 && prePosition == 0) {
prePosition = position;
return;
}
callBackIndicatotPosition(toRealPosition(position));
prePosition = position;
}
}
@Override
public void onPageScrollStateChanged(int state) {
PagerAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 1) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int count = adapter.getCount();
if (getCurrentItem() == 0) {
setCurrentItem(count - 2,false);
} else if (getCurrentItem() == count - 1) {
setCurrentItem(1,false);
}
}
}
}
};OnPageChangeListener mOnPagerChangeListener = new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
if (null != getAdapter() && getAdapter().getCount() > 1) {
int count = getAdapter().getCount();
if (position == 1 && prePosition == count - 1) {
prePosition = position;
return;
}else if (position == count - 2 && prePosition == 0) {
prePosition = position;
return;
}
callBackIndicatotPosition(toRealPosition(position));
prePosition = position;
}
}
@Override
public void onPageScrollStateChanged(int state) {
PagerAdapter adapter = getAdapter();
if (adapter != null && adapter.getCount() > 1) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
int count = adapter.getCount();
if (getCurrentItem() == 0) {
setCurrentItem(count - 2,false);
} else if (getCurrentItem() == count - 1) {
setCurrentItem(1,false);
}
}
}
}
};
如代码所以,每次滑动页面调用pagerSelected的时候。当不是边界处理的时候,都要重新调用callBackIndicatotPosition(toRealPosition(position))这个方法,我们来看一下这个方法。
/**
* 用于回调指示器的位置
* @param position
*/
private void callBackIndicatotPosition(int position) {
if (null != pageChangeListeners && position >= 0) {
for (OnPageChangeListener temp : pageChangeListeners) {
temp.onPageSelected(position);
}
}
};
/**
* 计算出指示器正确的显示位置
*/
private int toRealPosition(int position) {
if (getAdapter() != null && getAdapter().getCount() > 1) {
int count = getAdapter().getCount();
if (position == count - 1) {
return 0;
}else if (position == 0) {
return count - 3;
}else{
return position - 1;
}
}
return 0;
}
* 用于回调指示器的位置
* @param position
*/
private void callBackIndicatotPosition(int position) {
if (null != pageChangeListeners && position >= 0) {
for (OnPageChangeListener temp : pageChangeListeners) {
temp.onPageSelected(position);
}
}
};
/**
* 计算出指示器正确的显示位置
*/
private int toRealPosition(int position) {
if (getAdapter() != null && getAdapter().getCount() > 1) {
int count = getAdapter().getCount();
if (position == count - 1) {
return 0;
}else if (position == 0) {
return count - 3;
}else{
return position - 1;
}
}
return 0;
}
其实这个方法就是用来回调指示器位置的,外部需要设置一个pagerListener,每次滑动页面完成的时候,都会回调外面的pagerselected方法来让指示器显示在正确的位置。当边界处理的时候,这里就不需要了。比如从倒数第二张广告图(实际上显示的是最后一张广告图)向右滑动最后一张图片(实际显示的是第一张图片)。这时候通过callBackIndicatotPosition方法指示器位置回回到0的位置。与此同时我们看看onPageScrollStateChanged方法,当然getCurrentItem() == count - 1的时候,我们直接调用setCurrentItem(1,false)这个方法,图片重新回到了第1的位置,也就是显示第一张广告图片的位置(第0的位置显示最后一张广告图),指示器也是0的位置,此时刚好全部回到初始位置了。
同时,我们看看selected中的边界处理方法,当从显示最后一个位置的图片到直接显示第1位置的图片的时候,这时候指示器的位置就不需要重新设置了。因为从倒数第二个位置滑动到倒数第一个位置的时候,指示器的位置已经重新回0了。对应的代码就是
if (position == 1 && prePosition == count - 1) {
prePosition = position;
return;
}
prePosition = position;
return;
}
同样,当从图片1的位置向左滑显示图片0的时候,我们也是要做一下相应的边界处理。
3.注意的点
关于注意的点也不多,其实就是在写Adapter的时候,重写getCount方法的时候,我们需要真实的图片数量加2。然后在instantiateItem中给0位置和最后一个位置分别设置最后一张图片和第一张图片。
public int getCount() {
int count = mImageViews.size();
if (mImageViews.size() > 1) {
return count + 2;
} else {
return count;
}
}
public Object instantiateItem(ViewGroup container, int position) {
int realPosition = position;
int count = mImageViews.size();
if (count > 0) {
if (position == 0) {
realPosition = count - 1;
}else if (position == getCount() - 1) {
realPosition = 0;
} else {
realPosition = position - 1;
}
}
int Drawable = mImageViews.get(realPosition);
if (null == mViews.get(position)) {
onCreatView(position,Drawable);
}
ImageView view = mViews.get(position);
view.setImageResource(Drawable);
container.removeView(view);
container.addView(view);
return view;
}
int count = mImageViews.size();
if (mImageViews.size() > 1) {
return count + 2;
} else {
return count;
}
}
public Object instantiateItem(ViewGroup container, int position) {
int realPosition = position;
int count = mImageViews.size();
if (count > 0) {
if (position == 0) {
realPosition = count - 1;
}else if (position == getCount() - 1) {
realPosition = 0;
} else {
realPosition = position - 1;
}
}
int Drawable = mImageViews.get(realPosition);
if (null == mViews.get(position)) {
onCreatView(position,Drawable);
}
ImageView view = mViews.get(position);
view.setImageResource(Drawable);
container.removeView(view);
container.addView(view);
return view;
}
最后设置轮播的定时轮播的那些就比较简单,就不做介绍了。大家都会的了,用Handler设置就行了。项目里是封装了一个将各个事件处理都封装在一个LooperPagerView里面,外面设置调用然后设置相应的数据就可以了,指示器也可以根据需要自己去自定义一个相应的view来显示。
今天就写到这里,随便写了一点,当做记录一下吧....
项目地址:点击打开链接