ViewPager是一个常用的android组件,不过通常我们使用ViewPager的时候不能实现左右无限循环滑动,在滑到边界的时候会看到一个不能翻页的动画,可能影响用户体验。此外,某些区域性的ViewPager(例如展示广告或者公告之类的ViewPager),可能需要自动轮播的效果,即用户在不用滑动的情况下就能够看到其他页面的信息。

为此我查阅了网络上现有的一些关于实现这样效果的例子,但都不是很满意,经过反复实验,在这里总结并分享给大家,希望能有所帮助。

循环滑动效果的实现:PagerAdapter

我们知道ViewPager自带的滑动效果非常出色,因此我们基本不需要处理这个滑动,只处理内容的显示。而内容的显示是由Adapter控制的,因此这里重点就是这个Adapter了。为简单起见,本例的每个View直接是一张图片。下面是Adapter的代码:

  1. private class ImageAdapter extends PagerAdapter{
  2. private ArrayList<ImageView> viewlist;
  3. public ImageAdapter(ArrayList<ImageView> viewlist) {
  4. this.viewlist = viewlist;
  5. }
  6. @Override
  7. public int getCount() {
  8. //设置成最大,使用户看不到边界
  9. return Integer.MAX_VALUE;
  10. }
  11. @Override
  12. public boolean isViewFromObject(View arg0, Object arg1) {
  13. return arg0==arg1;
  14. }
  15. @Override
  16. public void destroyItem(ViewGroup container, int position,
  17. Object object) {
  18. //Warning:不要在这里调用removeView
  19. }
  20. @Override
  21. public Object instantiateItem(ViewGroup container, int position) {
  22. //对ViewPager页号求模取出View列表中要显示的项
  23. position %= viewlist.size();
  24. if (position<0){
  25. position = viewlist.size()+position;
  26. }
  27. ImageView view = viewlist.get(position);
  28. //如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
  29. ViewParent vp =view.getParent();
  30. if (vp!=null){
  31. ViewGroup parent = (ViewGroup)vp;
  32. parent.removeView(view);
  33. }
  34. container.addView(view);
  35. //add listeners here if necessary
  36. return view;
  37. }
  38. }

这里有几个地方需要注意:

  • getCount() 方法的返回值:这个值直接关系到ViewPager的“边界”,因此当我们把它设置为Integer.MAX_VALUE之后,用户基本就看不到这个边界了(估计滑到这里的时候电池已经挂了吧o_O)。当然,通常情况下设置为100倍实际内容个数也是可以的,之前看的某个实现就是这么干的。

  • instantiateItem() 方法position的处理:由于我们设置了count为 Integer.MAX_VALUE,因此这个position的取值范围很大很大,但我们实际要显示的内容肯定没这么多(往往只有几项),所以这里肯定会有求模操作。但是,简单的求模会出现问题:考虑用户向左滑的情形,则position可能会出现负值。所以我们需要对负值再处理一次,使其落在正确的区间内。

  • instantiateItem() 方法父组件的处理:通常我们会直接addView,但这里如果直接这样写,则会抛出IllegalStateException。假设一共有三个view,则当用户滑到第四个的时候就会触发这个异常,原因是我们试图把一个有父组件的View添加到另一个组件。但是,如果直接写成下面这样:

  1. (ViewGroup)view.getParent().removeView(view);

则又会因为一开始的时候组件并没有父组件而抛出NullPointerException。因此,需要进行一次判断。也就是上面的代码。

  • destroyItem() 方法:由于我们在instantiateItem()方法中已经处理了remove的逻辑,因此这里并不需要处理。实际上,实验表明这里如果加上了remove的调用,则会出现ViewPager的内容为空的情况。

轮播效果的实现:使用Handler进行更新

这里我定义了一个Handler来处理ViewPager的轮播。所谓的“轮播”效果实现起来是这样的:每隔一定时间(这里是3秒)切换一次显示的页面。通过控制各页面以一定顺序循环播放,就达到了轮播的效果。为此,我们可以使用Handler的sendEmptyMessageDelayed()方法来实现定时更新,并

注意用户也可能会对带有轮播效果的ViewPager手动进行滑动操作,因此我认为用户这时候是希望查看指定页面的,这时候应该取消轮播。下面是这个Handler的实现:

  1. private static class ImageHandler extends Handler{
  2. /**
  3. * 请求更新显示的View。
  4. */
  5. protected static final int MSG_UPDATE_IMAGE  = 1;
  6. /**
  7. * 请求暂停轮播。
  8. */
  9. protected static final int MSG_KEEP_SILENT   = 2;
  10. /**
  11. * 请求恢复轮播。
  12. */
  13. protected static final int MSG_BREAK_SILENT  = 3;
  14. /**
  15. * 记录最新的页号,当用户手动滑动时需要记录新页号,否则会使轮播的页面出错。
  16. * 例如当前如果在第一页,本来准备播放的是第二页,而这时候用户滑动到了末页,
  17. * 则应该播放的是第一页,如果继续按照原来的第二页播放,则逻辑上有问题。
  18. */
  19. protected static final int MSG_PAGE_CHANGED  = 4;
  20. //轮播间隔时间
  21. protected static final long MSG_DELAY = 3000;
  22. //使用弱引用避免Handler泄露.这里的泛型参数可以不是Activity,也可以是Fragment等
  23. private WeakReference<MainActivity> weakReference;
  24. private int currentItem = 0;
  25. protected ImageHandler(WeakReference<MainActivity> wk){
  26. weakReference = wk;
  27. }
  28. @Override
  29. public void handleMessage(Message msg) {
  30. super.handleMessage(msg);
  31. Log.d(LOG_TAG, "receive message " + msg.what);
  32. MainActivity activity = weakReference.get();
  33. if (activity==null){
  34. //Activity已经回收,无需再处理UI了
  35. return ;
  36. }
  37. //检查消息队列并移除未发送的消息,这主要是避免在复杂环境下消息出现重复等问题。
  38. if (activity.handler.hasMessages(MSG_UPDATE_IMAGE)){
  39. activity.handler.removeMessages(MSG_UPDATE_IMAGE);
  40. }
  41. switch (msg.what) {
  42. case MSG_UPDATE_IMAGE:
  43. currentItem++;
  44. activity.viewPager.setCurrentItem(currentItem);
  45. //准备下次播放
  46. activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
  47. break;
  48. case MSG_KEEP_SILENT:
  49. //只要不发送消息就暂停了
  50. break;
  51. case MSG_BREAK_SILENT:
  52. activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
  53. break;
  54. case MSG_PAGE_CHANGED:
  55. //记录当前的页号,避免播放的时候页面显示不正确。
  56. currentItem = msg.arg1;
  57. break;
  58. default:
  59. break;
  60. }
  61. }
  62. }

集成代码:MainActivity

下面是MainActivity的代码,主要是加载View和对ViewPager进行初始化设置。因为代码量比较少,重要的部分已经加了注释,就不赘述了

  1. public class MainActivity extends Activity {
  2. private static final String LOG_TAG = "MainActivity";
  3. private ImageHandler handler = new ImageHandler(new WeakReference<MainActivity>(this));
  4. private ViewPager viewPager;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. //初始化iewPager的内容
  10. viewPager = (ViewPager) findViewById(R.id.main_viewpager);
  11. LayoutInflater inflater = LayoutInflater.from(this);
  12. ImageView view1 = (ImageView) inflater.inflate(R.layout.item, null);
  13. ImageView view2 = (ImageView) inflater.inflate(R.layout.item, null);
  14. ImageView view3 = (ImageView) inflater.inflate(R.layout.item, null);
  15. view1.setImageResource(R.drawable.ics);
  16. view2.setImageResource(R.drawable.jellybean);
  17. view3.setImageResource(R.drawable.kitkat);
  18. ArrayList<ImageView> views = new ArrayList<ImageView>();
  19. views.add(view1);
  20. views.add(view2);
  21. views.add(view3);
  22. viewPager.setAdapter(new ImageAdapter(views));
  23. viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
  24. //配合Adapter的currentItem字段进行设置。
  25. @Override
  26. public void onPageSelected(int arg0) {
  27. handler.sendMessage(Message.obtain(handler, ImageHandler.MSG_PAGE_CHANGED, arg0, 0));
  28. }
  29. @Override
  30. public void onPageScrolled(int arg0, float arg1, int arg2) {
  31. }
  32. //覆写该方法实现轮播效果的暂停和恢复
  33. @Override
  34. public void onPageScrollStateChanged(int arg0) {
  35. switch (arg0) {
  36. case ViewPager.SCROLL_STATE_DRAGGING:
  37. handler.sendEmptyMessage(ImageHandler.MSG_KEEP_SILENT);
  38. break;
  39. case ViewPager.SCROLL_STATE_IDLE:
  40. handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY);
  41. break;
  42. default:
  43. break;
  44. }
  45. }
  46. });
  47. viewPager.setCurrentItem(Integer.MAX_VALUE/2);//默认在中间,使用户看不到边界
  48. //开始轮播效果
  49. handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY);
  50. }//end of onCreate
  51. }//e

viewpager循环滚动和自动轮播的问题的更多相关文章

  1. Xamarin自定义布局系列——支持无限滚动的自动轮播视图CarouselView

    背景简述 自动轮播视图(CarouselView)在现在App中的地位不言而喻,绝大多数的App中都有类似的视图,无论是WebApp还是Native App.在安卓.iOS以及Windows(UWP) ...

  2. ViewPager循环滚动

    一.先写个适配器 public class MyPagerAdapter extends PagerAdapter { /** * 上下文 */ private Context context; /* ...

  3. ViewPager自动轮播

    Android使用ViewPager实现左右循环滑动及轮播效果   ViewPager是一个常用的android组件,不过通常我们使用ViewPager的时候不能实现左右无限循环滑动,在滑到边界的时候 ...

  4. 使用ViewPager实现自动轮播

    很多APP中都实现了类似引导页的自动轮播,不由得想到昨天的引导页上修改一下代码实现轮播. 其实大体上只需要添加一个线程循环执行就可以了. 项目已同步至:https://github.com/nanch ...

  5. 仿网易新闻 ViewPager 实现图片自动轮播

    新闻 App 首页最上方一般会循环播放热点图片,如下图所示. 本文主要介绍了利用 ViewPager 实现轮播图片,图片下方加上小圆点指示器标记当前位置,并利用 Timer+Handler 实现了自动 ...

  6. ios - 图片自动轮播定时器(NSTimer)以及消息循环模式简介

    本文只是演示如何设置图片轮播的定时器. 创建全局变量NSTimer 程序启动后就开始轮播图片,所以在- (void)viewDidLoad中就启动定时器. 将定时器放入消息循环池中.- (void)v ...

  7. 20 ViewPager Demo4自动轮播

    MainActivity.java 思想:才用非常大的数 让其看起来可以循环轮播图片并且用户可以从尽头滑到首图的特点 . package com.qf.day20_viewpager_demo4; i ...

  8. 详细分析Android viewpager 无限循环滚动图片

    由于最近在忙于项目,就没时间更新博客了,于是趁着周日在房间把最近的在项目中遇到的技术总结下.最近在项目中要做一个在viewpager无限滚动图片的需求,其实百度一下有好多的例子,但是大部分虽然实现了, ...

  9. Android实现真正的ViewPager【平滑过渡】+【循环滚动】!!!顺带还有【末页跳转】。

    实现真正的ViewPager[平滑过渡]+[循环滚动]!!!顺带还有[末页跳转]. 首先呢, 我要对网上常见的3种ViewPager的循环滚动方法做个概述.急需看真正实现方法的同志请选择性忽略下面这一 ...

随机推荐

  1. Jmeter(十七)_驱动浏览器做GUI测试

    jmeter不光可以完成性能测试.接口测试,现在也可以依靠WebDriver来完成GUI的功能自动化测试了,是不是很神奇? 1:下载JMeterPlugins-WebDriver-1.3.1.zip, ...

  2. PTA中如何出Java题目?

    PTA中如何出Java题目? 很多第一次出题的老师,不知道Java在PTA中是如何处理输入的.写一篇文章供大家参考.比如以下这样的一个题目: 从控制台读入两个数,然后将其相加输出. 对于该题可以有如下 ...

  3. RX系列一 | ReactiveX根源 | 观察者模式分析

    RX系列一 | ReactiveX根源 | 观察者模式分析 Rx的响应式编程算是很火了,对吧,但是我的工作基本上就不会接触,所以学习的比较晚,到现在才分享给大家,我们一点点的去学,当你看完这整个系列的 ...

  4. MySQL连接及基本信息查看命令小结

    前言 学习PHP就不得不提MySQL,虽然有phpMyadmin这样的工具可以图形化操作数据库,但我还是想借学习PHP的机会使用下命令行方式操作数据库.以下就是我的学习小结,包括命令行连接数据库,查看 ...

  5. PGM:贝叶斯网的参数估计2

    http://blog.csdn.net/pipisorry/article/details/52599321 没时间看了,下次再看... 具有共享参数的学习模型 全局参数共享 局部参数共享 具有 共 ...

  6. ejabberd编译更新脚本

    ejabberd编译更新脚本 (金庆的专栏 2016.8) 用rebar编译ejabberd源码,然后复制编译所得beam文件到ejabberd安装目录, 调用ejabberdctl热更新. call ...

  7. Apache shiro集群实现 (八) web集群时session同步的3种方法

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  8. Java 资源本地化与国际化

    资源包 在编写应用程序的时候,需要面对的一个问题是如何来处理与locale相关的一些信息.比如,页面上的一些静态文本就希望能够以用户习惯的语言显示.最原始的做法是将这些信息硬编码到程序中(可能是一大串 ...

  9. Java安全管理器——SecurityManager

    总的来说,Java安全应该包括两方面的内容,一是Java平台(即是Java运行环境)的安全性:二是Java语言开发的应用程序的安全性.由于我们不是Java本身语言的制定开发者,所以第一个安全性不需要我 ...

  10. Afianl加载网络图片(续)

    上一篇已经讲了如何利用Afianl加载网络图片和下载文件,这篇文章将继续讲解使用Afinal加载网络图片的使用,主要结合listview的使用: 看效果图: listview在滑动过程中没用明显卡顿, ...