1.项目应用场景:官方提供的ViewPager不能循环滑动,当从左向右滑动到最后一页后,如果想再滑到第一页,则必须再从右向左滑动到第一页。本项目就是为了解决此问题而产生。
2.项目源码解析(主要包括InfinitePagerAdapter.java和InfiniteViewPager.java文件)
首先自定义一个ViewPager
package com.antonyt.infiniteviewpager;
import android.content.Context;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
/**
* A {@link ViewPager} that allows pseudo-infinite paging with a wrap-around effect. Should be used with an {@link
* InfinitePagerAdapter}.
*/
public class InfiniteViewPager extends ViewPager {
public InfiniteViewPager(Context context) {
super(context);
}
public InfiniteViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setAdapter(PagerAdapter adapter) {
super.setAdapter(adapter);
// 在给ViewPager填充数据的时候初始化第一个显示的view
setCurrentItem(0);
}
@Override
public void setCurrentItem(int item) {
setCurrentItem(item, false);
}
@Override
public void setCurrentItem(int item, boolean smoothScroll) {<span style="font-family: Arial, Helvetica, sans-serif;"> </span>
<pre name="code" class="java"> // 这里是关键,将当前页面所处的位置对页面实际总数求余数,同时加上页面总数的100倍
item = getOffsetAmount() + (item % getAdapter().getCount());
super.setCurrentItem(item, smoothScroll);
}
</pre><pre name="code" class="java"><pre name="code" class="java"> // 获取当前页面的实际位置
@Override public int getCurrentItem() {
int position = super.getCurrentItem();
if (getAdapter() instanceof InfinitePagerAdapter) {
InfinitePagerAdapter infAdapter = (InfinitePagerAdapter) getAdapter();
return (position % infAdapter.getRealCount());
} else {
return super.getCurrentItem();
}
}
</pre><pre name="code" class="java"><pre name="code" class="java"> // 设置可完整循环滑动的次数
private int getOffsetAmount() {
if (getAdapter() instanceof InfinitePagerAdapter) {
InfinitePagerAdapter infAdapter = (InfinitePagerAdapter) getAdapter();
return infAdapter.getRealCount() * 100;
} else {
return 0;
}
}
}
从上可以看出,主要是重写
setCurrentItem(int item, boolean smoothScroll)函数,给当前页面的位置赋予一个新的索引值,为循环滑动留出足够的控件。然后就是增加了一个设置循环滑动次数的方法
getOffsetAmount(),上例是页面实际总数的100倍。
</pre><pre name="code" class="java">
接着是自定义一个<span class="pl-e" style="box-sizing: border-box; color: rgb(121, 93, 163); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px; white-space: pre;">PagerAdapter</span><span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px; white-space: pre;"> ,源码如下:</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px; white-space: pre;"></span><pre name="code" class="java">package com.antonyt.infiniteviewpager;
import android.database.DataSetObserver;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
/**
* A PagerAdapter that wraps around another PagerAdapter to handle paging wrap-around.
*/
public class InfinitePagerAdapter extends PagerAdapter {
private static final String TAG = "InfinitePagerAdapter";
private static final boolean DEBUG = true;
private PagerAdapter adapter;
public InfinitePagerAdapter(PagerAdapter adapter) {
this.adapter = adapter;
}
@Override
public int getCount() {
<pre name="code" class="java" style="color: rgb(51, 51, 51); line-height: 16.799999237060547px;"> // 填充ViewPager的的视图的虚拟总数
return Integer.MAX_VALUE; } /** *
// 返回填充ViewPager的的视图的实际总数
*/ public int getRealCount() {
return adapter.getCount();
}
@Override public Object instantiateItem(ViewGroup container, int position) {
int virtualPosition = position % getRealCount();
debug("instantiateItem: real position: " + position);
debug("instantiateItem: virtual position: " + virtualPosition);
// only expose virtual position to the inner adapter
return adapter.instantiateItem(container, virtualPosition);
}
@Override public void destroyItem(ViewGroup container, int position, Object object) {
int virtualPosition = position % getRealCount();
debug("destroyItem: real position: " + position);
debug("destroyItem: virtual position: " + virtualPosition);
// only expose virtual position to the inner adapter
adapter.destroyItem(container, virtualPosition, object);
}
/*
* Delegate rest of methods directly to the inner adapter.
*/
@Override public void finishUpdate(ViewGroup container) {
adapter.finishUpdate(container);
}
@Override public boolean isViewFromObject(View view, Object object) {
return adapter.isViewFromObject(view, object);
}
@Override public void restoreState(Parcelable bundle, ClassLoader classLoader) {
adapter.restoreState(bundle, classLoader);
}
@Override public Parcelable saveState() {
return adapter.saveState();
}
@Override public void startUpdate(ViewGroup container) {
adapter.startUpdate(container);
}
@Override public CharSequence getPageTitle(int position) {
int virtualPosition = position % getRealCount();
return adapter.getPageTitle(virtualPosition);
}
@Override public float getPageWidth(int position) {
return adapter.getPageWidth(position);
}
@Override public void setPrimaryItem(ViewGroup container, int position, Object object) {
adapter.setPrimaryItem(container, position, object);
}
@Override public void unregisterDataSetObserver(DataSetObserver observer) {
adapter.unregisterDataSetObserver(observer);
}
@Override public void registerDataSetObserver(DataSetObserver observer) {
adapter.registerDataSetObserver(observer);
}
@Override public void notifyDataSetChanged() {
adapter.notifyDataSetChanged();
}
@Override public int getItemPosition(Object object) {
return adapter.getItemPosition(object);
}
/*
* End delegation
*/
private void debug(String message) {
if (DEBUG) {
Log.d(TAG, message);
}
}
}
从上可以看出:关键在于重写
getCount()函数,令它返回一个固定的足够大的整数,然后增加
getRealCount()方法,获取实际填充ViewPager的总数,之后就是将页面所处的虚拟位置的索引转换成实际位置的索引。
<span style="color: rgb(51, 51, 51); line-height: 16.799999237060547px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;">
</span>
<span style="color: rgb(51, 51, 51); line-height: 16.799999237060547px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;">
</span>
<span style="color: rgb(51, 51, 51); line-height: 16.799999237060547px; font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;">综上所述:实现循环滑动的原理就是在对ViewPager初始化的时候,给它填充固定数目的足够多的虚拟的页面空间,在视图渲染的时候利用虚拟位置对实际页面总数的求余实现虚拟位置与实际位置的转换。</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">
</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">举个例子:假设实际有4个页面需要滑动,编号为0,1,2,3. 令</span><span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">getOffsetAmount()返回1000,那么0号页面的虚拟位置索引值就是1000,向右滑动3次,分别到达1001(1001%4=1号页面),1002(1002%4=2号页面),</span><span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">1003(1003%4=3号页面),此时处于3号页面,若继续向右滑动,则到达1004(1004%4=0号页面),如此继续滑动下去,便可实现循环滑动的效果。向左滑动也是一样。</span>
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">
</span>
使用方法:
</pre><pre name="code" class="java">1.在布局文件中嵌入<span style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: inherit; color: rgb(51, 51, 51); font-size: 14px; background-color: transparent;">InfiniteViewPager:</span>
<span style="font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: inherit; color: rgb(51, 51, 51); font-size: 14px; background-color: transparent;">
</span>
<span style="font-family:Consolas, Liberation Mono, Menlo, Courier, monospace;color:#333333;"><span style="font-size: 14px;"></span></span><pre name="code" class="html"><com.antonyt.infiniteviewpager.InfiniteViewPager
android:id="@+id/pager"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
2.给ViewPager设置适配器
<pre name="code" class="java">...
PagerAdapter wrappedAdapter = new InfinitePagerAdapter(adapter);
viewPager.setAdapter(wrappedAdapter);
...
项目github地址
<span style="color: rgb(51, 51, 51); font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; line-height: 16.799999237060547px;">
</span>