背景:

瞎扯一下:ViewPager在Android中经常用于作为一个图片浏览的展示控件,几乎很多的项目都会运用到这个控件,大家对其也一定不陌生。另外伴随这个控件的还有一个词汇:无限循环。

这个是本篇博客的目的。

需求:

ViewPager控件的应用需求一般有:

1.加载多张图片,可以无限左右切换进行浏览(重点)

2.能够自动定时进行切换,同时不影响用户手势触碰的体验(触碰时自动停止定时,由用户掌控;放开时重新自动定时切换)

3.切换时需要一个匀速缓慢的过程,而不是一闪而过

4.能够内置在滚动控件如ScrollView中,同时能够准确区分左右滑动切换和上下滑动滚动的手势

难点:

各个需求分别对应的各个难点如下:

1.左右无限循环

2.切换定时、取消定时和重启定时

3.匀速缓慢切换的过程

4.上下手势和左右手势的区分

分析:

难点对应的分析如下:

1.左右无限循环:

目前左右无限循环的方案在网上有多种,大概可以分为真左右无限循环和假象左右无限循环两种。本博客采用的是后者(关于前者的内容和原理大家有兴趣可以搜一下)。

为什么说是假象无限左右循环呢?因为这个方案达到无限左右循环的一个突破口是:自定义PagerAdapter,在其重写方法getCount()中,返回值不是数据源的size,而是Integer.MAX_VALUE,这就让Android系统在使用这个Adapter的时候,以为其数据源有Integer.MAX_VALUE项数据,从而在左右切换时能够不断的进行切换,称之为假象的原因正是如此。


2.切换定时、取消定时和重启定时:

切换定时:我们可以采用Handler的SentMessageDelayed机制,隔一定的时间发送一个切换信息进行定时切换。

取消定时:需要在自定义ViewPager控件中对手势操作进行监听,当被按下或者进行滑动时,清空Handler的切换消息队列

重启定时:当用户把手从ViewPager控件上拿开时,重新在Handler中加入Delayed消息即可完成重启定时

3.匀速缓慢切换的过程

默认的ViewPager切换是一闪而过的,所以要实现这个需求,我们需要做点什么:

自定义Scroller,在其startScroll方法中,填入需要过渡切换的时长,如mDuration;再利用相关反射方法,让ViewPager利用这个Scroller进行匀速缓慢切换。

4.上下手势和左右手势的区分

内置在诸如ScrollView中的ViewPager,如果不做一定的处理,那么ViewPager将会给用户带来极其糟糕的操作体验:除非近乎水平的滑动ViewPager,否则很难进行左右切换,却容易误触发ScrollView的上下滚动事件。

处理方案是:自定义ViewPager,在其中的dispatchTouchEvent事件中,对触摸事件进行拦截,当触发ACTION_DOWN、ACTION_MOVE时,要求父类控件(如ScrollView)不响应触摸事件:getParent().requestDisallowInterceptTouchEvent(true); 当触发ACTION_UP或ACTION_CANCEL时,重新恢复父类控件对触摸事件的响应:getParent().requestDisallowInterceptTouchEvent(false);

经过这样的处理之后:用户在Viewpager控件区域进行触摸,那么就只会产生左右切换事件而不会触发上下滚动事件。

代码:

代码只贴出关键部分,相关详情可下载附件进行查看

1.自定义的PagerAdapter:

public class InfiniteLoopViewPagerAdapter extends PagerAdapter {private PagerAdapter adapter;//该adapter为真正绑定数据源的适配器public InfiniteLoopViewPagerAdapter(PagerAdapter adapter) {super();this.adapter = adapter;}@Overridepublic int getCount() {return Integer.MAX_VALUE;//关键处}public int getRealCount() {return adapter.getCount();}public int getRealItemPosition(Object object) {return adapter.getItemPosition(object);}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {//取得实际的索引位置int realPosition = position % getRealCount();adapter.destroyItem(container, realPosition, object);}@Overridepublic Object instantiateItem(ViewGroup container, int position) {int realPosition = position % getRealCount();return adapter.instantiateItem(container, realPosition);}/* * start */@Overridepublic void finishUpdate(ViewGroup container) {adapter.finishUpdate(container);}@Overridepublic boolean isViewFromObject(View view, Object object) {return adapter.isViewFromObject(view, object);}@Overridepublic void restoreState(Parcelable state, ClassLoader loader) {adapter.restoreState(state, loader);}@Overridepublic Parcelable saveState() {return adapter.saveState();}@Overridepublic void startUpdate(ViewGroup container) {adapter.startUpdate(container);}
2.Handler的切换定时

private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {if (msg.what == 0) {this.removeMessages(0);//每次切换开始前,清空队列viewPager.setCurrentItem(viewPager.getCurrentItem() + 1, true);//进行切换this.sendEmptyMessageDelayed(0, flipDuration);//下一次切换定时}super.handleMessage(msg);}};handler.sendEmptyMessageDelayed(0, flipDuration);//一般在ViewPager初始化完成后开始启动切换定时

Handler的取消定时和重启定时

public class InfiniteLoopViewPager extends ViewPager {private Handler handler;        private int flipDuration= 2*1000;public InfiniteLoopViewPager(Context context, AttributeSet attrs) {super(context, attrs);}public InfiniteLoopViewPager(Context context) {super(context);}public void setInfinateAdapter(Context context, Handler handler,PagerAdapter adapter,int flipDuration) {this.handler = handler;this.flipDuration = flipDuration;setAdapter(adapter);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {int action = ev.getAction();if (null == handler) {return true;}if (action == MotionEvent.ACTION_DOWN) {handler.removeMessages(0);//触摸按下时清空定时队列,停止切换} else if (action == MotionEvent.ACTION_MOVE) {handler.removeMessages(0);//触摸滑动时清空定时队列,停止切换} else if (action == MotionEvent.ACTION_UP) {handler.sendEmptyMessageDelayed(0, flipDuration);//触摸抬起时重启定时队列} else {handler.sendEmptyMessageDelayed(0, flipDuration);//其他情况,重启定时队列}return super.dispatchTouchEvent(ev);}}

3.匀速缓慢切换过程

自定义的Scroller:

public class FixedSpeedScroller extends Scroller {private int mDuration = 222;public FixedSpeedScroller(Context context) {super(context);}public FixedSpeedScroller(Context context, Interpolator interpolator) {super(context, interpolator);}@Overridepublic void startScroll(int startX, int startY, int dx, int dy, int duration) {// Ignore received duration, use fixed one insteadsuper.startScroll(startX, startY, dx, dy, mDuration);}@Overridepublic void startScroll(int startX, int startY, int dx, int dy) {// Ignore received duration, use fixed one insteadsuper.startScroll(startX, startY, dx, dy, mDuration);}public void setmDuration(int time) {mDuration = time;}public int getmDuration() {return mDuration;}

让ViewPager匀速缓慢切换:

private void initViewPagerSmoothScroll() {try {Field mField = ViewPager.class.getDeclaredField("mScroller");mField.setAccessible(true);mScroller = new FixedSpeedScroller(viewPager.getContext(),new AccelerateInterpolator());//mScroller即为自定义的Scroller实例mField.set(viewPager, mScroller);} catch (Exception e) {e.printStackTrace();}}

4.上下手势和左右手势的区分

 public class InfiniteLoopViewPager extends ViewPager {public InfiniteLoopViewPager(Context context, AttributeSet attrs) {super(context, attrs); }  public InfiniteLoopViewPager(Context context) {super(context); }public void setInfinateAdapter(Context context, Handler handler,PagerAdapter adapter,int flipDuration) {this.handler = handler;this.flipDuration = flipDuration;setAdapter(adapter);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {int action = ev.getAction();if (null == handler) {return true;}if (action == MotionEvent.ACTION_DOWN) {getParent().requestDisallowInterceptTouchEvent(true);//按下:要求父类控件不要响应滑动操作} else if (action == MotionEvent.ACTION_MOVE) {getParent().requestDisallowInterceptTouchEvent(true);//滑动:要求父类控件不要响应滑动操作} else if (action == MotionEvent.ACTION_UP) {getParent().requestDisallowInterceptTouchEvent(false);//抬起:父类控件可以响应滑动操作} else {        getParent().requestDisallowInterceptTouchEvent(false);//其他:父类控件可以响应滑动操作}return super.dispatchTouchEvent(ev);}}


附件:demo下载

http://download.csdn.net/detail/junjun071308/9120935

更多相关文章

  1. 关于android的零碎问题的整理
  2. Android(安卓)XML 中schema和自定义属性的关系
  3. 《Android开发艺术探索》之学习笔记(三)View的基础知识
  4. Android(安卓)(最新)控件透明度,布局透明度,Activity透明度,颜色透
  5. 初学Android,手势翻页效果(四十九)
  6. [Android(安卓)Studio]掌握Android(安卓)Studio的五种常见控件和
  7. 第3章 UI
  8. android 仿ios开关控件
  9. Android中ListView分页加载数据功能实现

随机推荐

  1. TabHost页卡
  2. Android(安卓)Handler 深入学习(1)
  3. Debug with android ndk-gdb
  4. Androd开发艺术探索 第10章 Android的消
  5. Android SDK 和 杂7杂8
  6. android Settings中的各个默认设置
  7. Android 编译NDK
  8. Android 初识Retrofit
  9. TabHost两种实现方式
  10. android 命令(adb shell)进入指定模拟器