ViewPager是v4支持库中的一个控件,相信几乎所有接触Android开发的人都对它不陌生。之所以还要在这里翻旧账,是因为我在最近的项目中有多个需求用到了它,觉得自己对它的认识不够深刻。我计划从最简单的使用场景出发,记录我到目前为止所对ViewPager的使用情况以及有关它的一些知识点。

这个系列的代码将存放在Github仓库中,每篇文章对应一个分支或几个分支。

这是第三篇文章,将讨论集中有关如何使用ViewPager展示无限循环视图的方法。

方法1:极大化PagerAdapter.getCount的返回值

这是最简单的实现方法。关键在于重写PagerAdapter.getCount方法,将其返回值设置为Integer.MAX_VALUE,然后通通过取模position%count的方式获取得对应的数据进行视图渲染。

  1. ...
  2. @Override
  3. public int getCount() {
  4. return Integer.MAX_VALUE;
  5. }
  6.  
  7. @Override
  8. public Object instantiateItem(ViewGroup container, int position) {
  9. int index = position % 3;
  10. String text = texts.get(index);
  11. TextView textView = new TextView(container.getContext());
  12. textView.setText(text);
  13. container.addView(textView);
  14. return textView;
  15. }
  16. ...

这种方法毕竟不是真实的无限循环,只是虚拟了一个极大的页数,让用户翻页的时候很触及到“世界的尽头”。所以在初始化的时候需要完成一个关键初始化:

  1. viewPager.setCurrentItem(Integer.MAX_VALUE / 2, false);

把初始化页面定位到世界的中央。

相关代码在分支:03-fake-infinite-cycle可以获取。

方法2:在数据源首尾添加重复节点

这是实现ViewPager无限循环的另一种方案:通过在数据源的首尾处添加重复的数据(在源数据前插入最后一个数据,其后插入原来的第一个数据),这两个重复数据的作用是在滚动过程中作为中间视图,当滚动停止时立刻切换到最终的视图,进入下一个滚动循环。

相关代码见分支:03-infinite-cycle-with-additional-views

首先在往PagerAdapter插入数据的时候对数据进行一下处理:

  1. public void setTexts(List<String> texts) {
  2. this.texts.clear();
  3. if (texts == null) {
  4. notifyDataSetChanged();
  5. return;
  6. }
  7.  
  8. // 只有一个数据时不循环
  9. if (texts.size() == 1) {
  10. this.texts.addAll(texts);
  11.  
  12. // 多个数据,插入重复数据
  13. } else if (texts.size() > 1) {
  14. this.texts.add(texts.get(texts.size() - 1));
  15. this.texts.addAll(texts);
  16. this.texts.add(texts.get(0));
  17. }
  18.  
  19. notifyDataSetChanged();
  20. }

其次让ViewPager实现ViewPager.OnPageChangeListener接口,监听滚动状态。代码如下:

  1. @Override
  2. public void onPageSelected(int position) {
  3. int realCount = getCount() - 2;
  4. // 多于1,才会循环跳转
  5. if ( getCount() > 1) {
  6. // 首位之前,跳转到末尾(N)
  7. if ( position < 1) {
  8. position = realCount;
  9. viewPager.setCurrentItem(position,false);
  10. }
  11. // 末位之后,跳转到首位(1)
  12. else if ( position > realCount) {
  13. position = 1;
  14. viewPager.setCurrentItem(position,false);
  15. }
  16. }
  17. }

最后组装一下ViewPagerPagerAdapter即可:

  1. viewPager.setAdapter(adapter);
  2. viewPager.addOnPageChangeListener(adapter);
  3. if (adapter.getCount() > 1) {
  4. viewPager.setCurrentItem(1, false);
  5. }

注意最后的if语句,它让ViewPager默认显示第一页。否则页面将展示最后一个源数据的内容且无法向右滑动。

实际上这种方法也是有缺陷的。当用户滑动ViewPager到源数据的最后一个节点(下标:getCount()-2)并且先要继续滑动显示下一个节点时,这期间ViewPager首先随用户手指一动正常展示我们插入的重复内容(下标:getCount()-1),当滚动停止且触发了onPageSelected回调,ViewPager立即切换到源数据的第一页(下标:1)进入下一个循环。这会导致几个不协调的现象:

  1. 切换到下一个循环的时候会破坏ViewPager的滚动动画(如:滚动惯性动画)。
  2. 切换前展示的缓存视图在切换时被销毁,切换后的视图需要重新生成。如果这里有需要延迟加载的内容也会导致展示不协调。
方法3:改进方法2

针对上述方法2提出的两个缺点,在此将着重解决缺点1出现的动画不连贯的现象,作为第三种方案进行介绍。至于缺点2可以通过缓存视图的方式解决,就不在此赘述。

方法3的代码见分支:03-infinite-cycle-better-practise

该方案已经满足我目前的需求。它的关键点如下:

首先,如方法2一样在数据源头尾插入重复节点,用于过渡。这里我重新写了setTexts方法,让只有一个数据的场景也可以循环:

  1. public void setTexts(List<String> texts) {
  2. this.count = 0;
  3. this.texts.clear();
  4. if (texts != null && texts.size() > 0) {
  5. this.count = texts.size();
  6. for (int i = 0; i <= count + 1; i++) {
  7. if (i == 0) {
  8. this.texts.add(texts.get(count - 1));
  9. } else if (i == count + 1) {
  10. this.texts.add(texts.get(0));
  11. } else {
  12. this.texts.add(texts.get(i - 1));
  13. }
  14. }
  15. }
  16. notifyDataSetChanged();
  17. }

接下来解决方法2的动画不连贯的问题。注意到在方法2中在OnPageChangeListeneronPageSelected方法中处理了循环的跳转逻辑。然后onPageSelectedViewPager处理ACTION_UP事件时回调的。也就是说,当用户的手指时快速拖动后离开ViewPager时,ViewPager回调了该方法,然后还会继续后续的衰减动画。在这个时间点使用setCurrentItem跳转到指定视图必然会造成动画停顿的问题。

把切换循环改在ViewPager的滚动状态发生变化时进行。怎么做呢?见代码:

  1. // count为源数据的条目
  2. // currentItem为PagerAdapter当前选中项
  3. @Override
  4. public void onPageSelected(int position) {
  5. currentItem = position;
  6. }
  7. @Override
  8. public void onPageScrollStateChanged(int state) {
  9. switch (state) {
  10. case ViewPager.SCROLL_STATE_IDLE://No operation
  11. if (currentItem == 0) {
  12. viewPager.setCurrentItem(count, false);
  13. } else if (currentItem == count + 1) {
  14. viewPager.setCurrentItem(1, false);
  15. }
  16. break;
  17. case ViewPager.SCROLL_STATE_DRAGGING: //start Sliding
  18. if (currentItem == 0) {
  19. viewPager.setCurrentItem(count, false);
  20. } else if (currentItem == count + 1) {
  21. viewPager.setCurrentItem(1, false);
  22. }
  23. break;
  24. case ViewPager.SCROLL_STATE_SETTLING://end Sliding
  25. break;
  26. }
  27. }

代码中在状态变为停止“SCROLL_STATE_IDLE”或状态变为开始滚动“SCROLL_STATE_DRAGGING”时处理了循环切换的逻辑。

这里描述一下整个流程。如果用户处于第一页且继续向右滑动手指,或者处于最后一页且继续向左滑动手指时,在状态由空闲变为开始滚动“SCROLL_STATE_DRAGGING”进行切换。第一种情况,如果最终成功切换到目标页面,那么在状态变为空闲时由于currentItem已经发生变化,所以不会重复切换。第二种情况,如果没有成功切换到目标页面,ViewPager需要在状态变为“SCROLL_STATE_IDLE”时再次切换回原来的视图。

注意在初始化ViewPager时调用一下setCurrentItem(1),让它正确显示第一个视图。

小结

ViewPager循环展示数据的方法目前就介绍到这里。我认为方法1和方法3根据不同场景考虑是否使用。出于某种情结,我更倾向于使用方法3,毕竟方法三是查看了github中的banner库之后总结出来的。

本文来自作者同步博客

ViewPager使用记录3——循环展示的更多相关文章

  1. ViewPager使用记录1——展示固定数据

    ViewPager是v4支持库中的一个控件,相信几乎所有接触Android开发的人都对它不陌生.之所以还要在这里翻旧账,是因为我在最近的项目中有多个需求用到了它,觉得自己对它的认识不够深刻.我计划从最 ...

  2. 【三石jQuery视频教程】01.图片循环展示

    视频地址:http://v.qq.com/page/e/5/t/e0149n5he5t.html 大家好,欢迎来到[三石jQuery视频教程],我是您的老朋友 - 三生石上. 今天,我们要通过基本的H ...

  3. 【三石jQuery视频教程】01.图片循环展示_再次重发

    之前的文章,由于在博文的底部放有微信公众号的缘故,被管理员判定为: 您好,您的这篇博文内容本身没什么问题,但是,在博文底部存在推广信息内容.... 你们也没告知到底是哪条触犯了博客园的规矩,我就把底部 ...

  4. Elastislide - 响应式的图片循环展示效果

    Elastislide 是一款非常优秀的响应式 jQuery 图片循环展示(旋转木马)插件,集成了 Touchwipe 插件以支持触屏设备.提供了四种效果:水平图片传送带.垂直图片传送带.固定在屏幕底 ...

  5. iOS开发UI篇—无限轮播(循环展示)

    iOS开发UI篇—无限轮播(循环展示) 一.简单说明 之前的程序还存在一个问题,那就是不能循环展示,因为plist文件中只有五个数组,因此第一个和最后一个之后就没有了,下面介绍处理这种循环展示问题的小 ...

  6. Echart饼形图和折线图的循环展示及选择展示

    需求:根据不同的入参调同一接口,循环展示一组饼形图或折线图: 主要问题:在于给定的数据格式不符合图表的配置项格式,需要拆分组装数据:首先默认展示几个图表,当选中一个类别,需要展示其中一个的时候,页面中 ...

  7. ViewPager使用记录2——展示动态数据

    ViewPager是v4支持库中的一个控件,相信几乎所有接触Android开发的人都对它不陌生.之所以还要在这里翻旧账,是因为我在最近的项目中有多个需求用到了它,觉得自己对它的认识不够深刻.我计划从最 ...

  8. SQL记录-PLSQL循环

    PL/SQL循环   可能有一种情况,当需要执行的代码块的几个多次.在一般情况下,语句顺序执行:一个函数的第一条语句,首先执行,然后是第二个...等等. 编程语言提供了各种控制结构,允许更多复杂的执行 ...

  9. MySQL通过游标来实现通过查询记录集循环

    /*我们有时候会遇到需要对 从A表查询的结果集S_S 的记录 进行遍历并做一些操作(如插入),且这些操作需要的数据或许部分来自S_S集合*//*临时存储过程,没办法,不能直接在查询窗口做这些事.*/d ...

随机推荐

  1. JAVA提高十:ArrayList 深入分析

    前面一章节,我们介绍了集合的类图,那么本节将学习Collection 接口中最常用的子类ArrayList类,本章分为下面几部分讲解(说明本章采用的JDK1.6源码进行分析,因为个人认为虽然JDK1. ...

  2. WinForm 菜单控件

    一:MenuStrip 菜单条 MenuStrip 是应用程序菜单条的容器. 二:ToolStripMenuItem 像上面图中, 文件 格式 等这些菜单当中的一级菜单以及文件中的 新建 打开 分割条 ...

  3. octave中的一些基本操作

    1.矩阵的表示:v = [1 2 2]  %表示1行3列的矩阵 v = [1; 2; 2] %表示3行1列的矩阵 v = [1 2; 2 3; 4 5] %3*2矩阵 size(v) % 求v的行与列 ...

  4. 在vue2.0中使用sass

    第一步:使用sass必须安装下面三个东西 cnpm install node-sass --save //安装node-sass cnpm install sass-loader --save //安 ...

  5. 2017CCPC秦皇岛G ZOJ 3987Numbers(大数+贪心)

    Numbers Time Limit: 2 Seconds      Memory Limit: 65536 KB DreamGrid has a nonnegative integer n . He ...

  6. SAXReader简单实例解析HTML

    转载自:http://blog.csdn.net/seayqrain/article/details/5024068# 使用SAXReader需要导入dom4j-full.jar包. dom4j是一个 ...

  7. 最最简单的CentOs6在线源搭建

    非常实用的在线源搭建,只要4步骤 1.点击进入http://mirrors.aliyun.com/repo/epel-6.repo ,这是阿里云的源 2.复制所有的代码  ctrl+a,ctrl+c ...

  8. easyui dialog 中嵌入html页面

    最近使用easyui比较多,这个插件确实很好用.在使用时也遇到了大大小小的问题,好在都一一解决了. 记录一下今天遇到的问题. 目的:用easyui的dialog嵌入一个html页面(html中仍有要执 ...

  9. Python基础-注释-变量赋值

    一.注释 # 注释 \n 行分隔符 \ 继续上一行 '''   *** ''' 多行注释 二.基本规则 : 分开代码块(组)   头$尾 缩进块  语句代码块  用缩进深度区分 空行     用于分割 ...

  10. 玩转 HTML5 下 WebGL 的 3D 模型交并补

    建设性的立体几何具有许多实际用途,它用于需要简单几何对象的情况下,或者数学精度很重要的地方,几乎所有的工程 CAD 软件包都使用 CSG(可以用于表示刀具切削,以及零件必须配合在一起的特征).CSG ...