Android开发常见的一个需求就是焦点图需要循环轮播,比如实际内容是5张焦点图,当在最后一张焦点图右滑时应该滑动至第一张焦点图,同样的,在第一张焦点图左滑应该滑动至最后一张焦点图。
原理与实现
网上有很多现成的包装好的类,只需要继承并且调用就行了,但是搞清楚它的原理很有必要。
一般焦点图是使用support v4提供的ViewPager来实现的,ViewPager的基本使用这里就不重复了,网上有很多。
这里我们使用一个基本的PagerAdapter来分析,改造成为轮播循环的ViewPager。
首先,使PagerAdapter#getCount()方法返回一个很大的数值,如
public int getCount() {
return Integer.MAX_VALUE;
}
我们返回了Integer的最大值,虽然它还是一个有限的值,但是可以视作无限大。
假设我们的实际内容为List<MyData> realData
,那我们在填充数据的时候需要使用下面的方法获取实际的数据位置
private int getRealPosition(int position) {
return position % realData.size();
}
虽然焦点图每一张图的内容不一样,但它的结构是一样的,即布局一样。
这时,我们可以利用ListView的思想,将滑出屏幕的View缓存起来。当需要新View时,先去缓存中取,如果取不到,则自己创建;当View滑出屏幕时,我们首先将其从View Hierarchy中移除,然后将其加入缓存中。
这里我们可以利用ListView的父类AbsListView里的静态类RecyclerBin作为缓存管理器, 国外开源大神Jake Wharton已经帮我们移植好了,可以看开源项目 - salvage。
有了RecyclerBin的协助,我们在PagerAdapter的instantiateItem和destoryItem方法中可以这样操作
public final Object instantiateItem(ViewGroup container, int position) {
int viewType = getItemViewType(position);
View view = null;
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
view = recycleBin.getScrapView(position, viewType);
}
view = getView(position, view, container);
container.addView(view);
return view;
}
public final void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
int viewType = getItemViewType(position);
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
recycleBin.addScrapView(view, position, viewType);
}
}
上面的getView方法其实就是参照ListAdapter的getView方法来创建的,使用方法一模一样。
至此,我们可以一直向右滑动了,但是初始状态却无法向左滑动,原因是初始状态时currentItem为0,这时,我们将currentItem设置为一个中间值即可,一般设为
Integer.MAX_VALUE / 2 - Integer.MAX_VALUE / 2 % REAL_SIZE
其中REAL_SIZE就是真实的焦点图数量
现在,左右均可以任意循环轮播了。
如果需要自动滚动,则可以使用handler发送消息一直滚动ViewPager即可。