自定义循环滑动的viewpager
摘要
今天和大家分享一下如何定制一个可以循环滑动的viewpager。其实今天更重要的提供一种组件化思想,当然你可以理解为面向对象思想。
吐槽一下网上流行的实现方式吧(为了方便说明,下文称之为方式A),方式A是重写adapter的getCount方法,返回一个很大的数(值为 max),adapter中的getView方法中的position重新根据实际数量取模,把viewpager设置在max二分之一的位置。因为这个 值很大所以基本不可能滑动到position=0或者position=max的位置,不过确切来说这并不是无限循环,采用脚本之类的应该可以滑动到边界 (没有试验过)。
我们的需求
快速把之前viewpager相关的代码改成无限循环。
这个需求很简单就是在已有的代码上完成无限循环。当然你可以根据方式A来完成,它是通过适配器来完成无限循环的,不过这种方式你需要改的代码并不少,毕竟你得改适配器。
本文需要提供的方式B就登场了,好处在于不用改你的adapter,只需要把你的viewpager改成本文中的组件CycleViewPager就可以了,其他的都不用改。
原理
CycleViewPager继承ViewPager。当向右翻页(文中指的向右翻页是指手指由右向左滑,向左翻页反之),数据滑动到最后一条数据 时调用setCurrentItem(0)返回第一条,当向左翻页,数据滑动到第一条数据时setCurrentItem(getCount()-1)返 回到最后一条数据。在设置数据的时候需要多生成两条数据,比如原始数据是[a,b,c,d,e],那么生成的数据为[e,a,b,c,d,e,a],原始 数据第一条前面加了最后一条数据e,原始数据的最后一条的后面加了原始数据的第一条数据a。从a滑到e时,继续向右翻页那么就到了数据a(最右边的那个 a),停止滑动时调用setCurrentItem(1)返回第一条数据a(最左边的那个a);从e滑动到a时,继续向左翻页那么就到了数据e(做左边的 那个e),停止滑动时调用setCurrentItem(5)。
直接上代码
1package com.vane.widget.cycleviewpager; 2 3import android.content.Context; 4import android.database.DataSetObserver; 5import android.support.v4.view.PagerAdapter; 6import android.support.v4.view.ViewPager; 7import android.util.AttributeSet; 8import android.view.View; 9import android.view.ViewGroup; 10 11publicclass CycleViewPager extends ViewPager { 12 13private InnerPagerAdapter mAdapter; 14 15public CycleViewPager(Context context) { 16super( context); 17 setOnPageChangeListener( null); 18 } 19 20public CycleViewPager(Context context, AttributeSet attrs) { 21super( context, attrs); 22 setOnPageChangeListener( null); 23 } 24 25 @Override 26publicvoid setAdapter(PagerAdapter arg0) { 27 mAdapter = new InnerPagerAdapter( arg0); 28super.setAdapter( mAdapter); 29 setCurrentItem( 1); 30 } 31 32 @Override 33publicvoid setOnPageChangeListener(OnPageChangeListener listener) { 34super.setOnPageChangeListener( new InnerOnPageChangeListener( listener)); 35 } 36 37privateclass InnerOnPageChangeListener implements OnPageChangeListener { 38 39private OnPageChangeListener listener; 40privateint position; 41 42public InnerOnPageChangeListener(OnPageChangeListener listener) { 43this.listener = listener; 44 } 45 46 @Override 47publicvoid onPageScrollStateChanged(int arg0) { 48if(null != listener) { 49 listener.onPageScrollStateChanged( arg0); 50 } 51if(arg0 == ViewPager.SCROLL_STATE_IDLE) { 52if(position == mAdapter.getCount() - 1) { 53 setCurrentItem( 1, false); 54 } 55elseif(position == 0) { 56 setCurrentItem( mAdapter.getCount() - 2, false); 57 } 58 } 59 } 60 61 @Override 62publicvoid onPageScrolled(int arg0, float arg1, int arg2) { 63if(null != listener) { 64 listener.onPageScrolled( arg0, arg1, arg2); 65 } 66 } 67 68 @Override 69publicvoid onPageSelected(int arg0) { 70 position = arg0; 71if(null != listener) { 72 listener.onPageSelected( arg0); 73 } 74 } 75 } 76 77privateclass InnerPagerAdapter extends PagerAdapter { 78 79private PagerAdapter adapter; 80 81public InnerPagerAdapter(PagerAdapter adapter) { 82this.adapter = adapter; 83 adapter.registerDataSetObserver( new DataSetObserver() { 84 85 @Override 86publicvoid onChanged() { 87 notifyDataSetChanged(); 88 } 89 90 @Override 91publicvoid onInvalidated() { 92 notifyDataSetChanged(); 93 } 94 95 }); 96 } 97 98 @Override 99publicint getCount() { 100return adapter.getCount() + 2; 101 } 102103 @Override 104publicboolean isViewFromObject(View arg0, Object arg1) { 105return adapter.isViewFromObject( arg0, arg1); 106 } 107108 @Override 109public Object instantiateItem(ViewGroup container, int position) { 110if(position == 0) { 111 position = adapter.getCount() - 1; 112 } 113elseif(position == adapter.getCount() + 1) { 114 position = 0; 115 } 116else { 117 position -= 1; 118 } 119return adapter.instantiateItem( container, position); 120 } 121122 @Override 123publicvoid destroyItem(ViewGroup container, int position, Object object) { 124 adapter.destroyItem( container, position, object); 125 } 126127 } 128 }
如何使用
使用方法和viewPager是一样一样的,你就认为是viewpager来使用就可以了。源码你可以在github上取https://github.com/vanezkw/cycleviewpager.git
总结
源码中大家也都看到了没有什么特别的其实就是平时讲的设计模式。之前提到的方式A个人认为并不完全是面向对象的方式,这个当然也看大家如何理解。