热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android使用ViewPager实现左右循环滑动及轮播效果

ViewPager是一个常用的Android组件,不过通常我们使用ViewPager的时候不能实现左右无限循环滑动,在滑到边界的时候会看到一个不能翻页的动画,可能影响用户体验,接下来通过本文给大家介绍Android使用ViewPager实现左右循环滑动及轮播效果,一起看看吧

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

循环滑动效果的实现:PagerAdapter

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

class ImageAdapter extends PagerAdapter {
 private ArrayList viewlist;
 public ImageAdapter(ArrayList viewlist) {
  this.viewlist = viewlist;
 }
 @Override
 public int getCount() {
  // 设置成最大,使用户看不到边界
  return Integer.MAX_VALUE;
 }
 @Override
 public boolean isViewFromObject(View arg0, Object arg1) {
  return arg0 == arg1;
 }
 @Override
 public void destroyItem(ViewGroup container, int position, Object object) {
  // Warning:不要在这里调用removeView
 }
 @Override
 public Object instantiateItem(ViewGroup container, int position) {
  // 对ViewPager页号求模取出View列表中要显示的项
  position %= viewlist.size();
  if (position <0) {
   position = viewlist.size() + position;
  }
  ImageView view = viewlist.get(position);
  // 如果View已经在之前添加到了一个父组件,则必须先remove,否则会抛出IllegalStateException。
  ViewParent vp = view.getParent();
  if (vp != null) {
   ViewGroup parent = (ViewGroup) vp;
   parent.removeView(view);
  }
  container.addView(view);
  // add listeners here if necessary
  return view;
 }
}

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

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

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

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

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

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

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

集成代码:MainActivity

简单布局 activity_main:


 
 

代码操作MainActivity:

public class MainActivity extends Activity {
 private static final String LOG_TAG = "MainActivity";
 private ImageHandler handler = new ImageHandler(new WeakReference(this));
 private ViewPager viewPager;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  // 初始化iewPager的内容
  viewPager = (ViewPager) findViewById(R.id.main_viewpager);
  //数据集
  ArrayList views = new ArrayList();
  for (int i = 0; i <7; i++) {
   ImageView view1 = new ImageView(this);
   if (i == 0) {
    view1.setImageResource(R.drawable.bargain_day1);
   } else if (i == 1) {
    view1.setImageResource(R.drawable.bargain_day2);
   } else if (i == 2) {
    view1.setImageResource(R.drawable.bargain_day3);
   } else if (i == 3) {
    view1.setImageResource(R.drawable.bargain_day4);
   } else if (i == 4) {
    view1.setImageResource(R.drawable.bargain_day5);
   } else if (i == 5) {
    view1.setImageResource(R.drawable.bargain_day6);
   } else if (i == 6) {
    view1.setImageResource(R.drawable.bargain_day7);
   }
   views.add(view1);
  }
  viewPager.setAdapter(new ImageAdapter(views));
  viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
   // 配合Adapter的currentItem字段进行设置。
   @Override
   public void onPageSelected(int arg0) {
    handler.sendMessage(Message.obtain(handler, ImageHandler.MSG_PAGE_CHANGED, arg0, 0));
   }
   @Override
   public void onPageScrolled(int arg0, float arg1, int arg2) {
   }
   // 覆写该方法实现轮播效果的暂停和恢复
   @Override
   public void onPageScrollStateChanged(int arg0) {
    switch (arg0) {
    case ViewPager.SCROLL_STATE_DRAGGING:
     handler.sendEmptyMessage(ImageHandler.MSG_KEEP_SILENT);
     break;
    case ViewPager.SCROLL_STATE_IDLE:
     handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY);
     break;
    default:
     break;
    }
   }
  });
  viewPager.setCurrentItem(Integer.MAX_VALUE / 2);// 默认在中间,使用户看不到边界
  // 开始轮播效果
  handler.sendEmptyMessageDelayed(ImageHandler.MSG_UPDATE_IMAGE, ImageHandler.MSG_DELAY);
 }// end of onCreate
 //加上动画滑动
 private static class ImageHandler extends Handler {
  /**
   * 请求更新显示的View。
   */
  protected static final int MSG_UPDATE_IMAGE = 1;
  /**
   * 请求暂停轮播。
   */
  protected static final int MSG_KEEP_SILENT = 2;
  /**
   * 请求恢复轮播。
   */
  protected static final int MSG_BREAK_SILENT = 3;
  /**
   * 记录最新的页号,当用户手动滑动时需要记录新页号,否则会使轮播的页面出错。
   * 例如当前如果在第一页,本来准备播放的是第二页,而这时候用户滑动到了末页,
   * 则应该播放的是第一页,如果继续按照原来的第二页播放,则逻辑上有问题。
   */
  protected static final int MSG_PAGE_CHANGED = 4;
  // 轮播间隔时间
  protected static final long MSG_DELAY = 3000;
  // 使用弱引用避免Handler泄露.这里的泛型参数可以不是Activity,也可以是Fragment等
  private WeakReference weakReference;
  private int currentItem = 0;
  protected ImageHandler(WeakReference wk) {
   weakReference = wk;
  }
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   Log.d(LOG_TAG, "receive message" + msg.what);
   MainActivity activity = weakReference.get();
   if (activity == null) {
    // Activity已经回收,无需再处理UI了
    return;
   }
   // 检查消息队列并移除未发送的消息,这主要是避免在复杂环境下消息出现重复等问题。
   if (activity.handler.hasMessages(MSG_UPDATE_IMAGE)) {
    activity.handler.removeMessages(MSG_UPDATE_IMAGE);
   }
   switch (msg.what) {
   case MSG_UPDATE_IMAGE:
    currentItem++;
    activity.viewPager.setCurrentItem(currentItem);
    // 准备下次播放
    activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
    break;
   case MSG_KEEP_SILENT:
    // 只要不发送消息就暂停了
    break;
   case MSG_BREAK_SILENT:
    activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
    break;
   case MSG_PAGE_CHANGED:
    // 记录当前的页号,避免播放的时候页面显示不正确。
    currentItem = msg.arg1;
    break;
   default:
    break;
   }
  }
 }
}

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

//加上动画滑动
 private static class ImageHandler extends Handler {
  /**
   * 请求更新显示的View。
   */
  protected static final int MSG_UPDATE_IMAGE = 1;
  /**
   * 请求暂停轮播。
   */
  protected static final int MSG_KEEP_SILENT = 2;
  /**
   * 请求恢复轮播。
   */
  protected static final int MSG_BREAK_SILENT = 3;
  /**
   * 记录最新的页号,当用户手动滑动时需要记录新页号,否则会使轮播的页面出错。
   * 例如当前如果在第一页,本来准备播放的是第二页,而这时候用户滑动到了末页,
   * 则应该播放的是第一页,如果继续按照原来的第二页播放,则逻辑上有问题。
   */
  protected static final int MSG_PAGE_CHANGED = 4;
  // 轮播间隔时间
  protected static final long MSG_DELAY = 3000;
  // 使用弱引用避免Handler泄露.这里的泛型参数可以不是Activity,也可以是Fragment等
  private WeakReference weakReference;
  private int currentItem = 0;
  protected ImageHandler(WeakReference wk) {
   weakReference = wk;
  }
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   Log.d(LOG_TAG, "receive message" + msg.what);
   MainActivity activity = weakReference.get();
   if (activity == null) {
    // Activity已经回收,无需再处理UI了
    return;
   }
   // 检查消息队列并移除未发送的消息,这主要是避免在复杂环境下消息出现重复等问题。
   if (activity.handler.hasMessages(MSG_UPDATE_IMAGE)) {
    activity.handler.removeMessages(MSG_UPDATE_IMAGE);
   }
   switch (msg.what) {
   case MSG_UPDATE_IMAGE:
    currentItem++;
    activity.viewPager.setCurrentItem(currentItem);
    // 准备下次播放
    activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
    break;
   case MSG_KEEP_SILENT:
    // 只要不发送消息就暂停了
    break;
   case MSG_BREAK_SILENT:
    activity.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
    break;
   case MSG_PAGE_CHANGED:
    // 记录当前的页号,避免播放的时候页面显示不正确。
    currentItem = msg.arg1;
    break;
   default:
    break;
   }
  }
 }

这里我定义了一个Handler来处理ViewPager的轮播。所谓的“轮播”效果实现起来是这样的:每隔一定时间(这里是3秒)切换一次显示的页面。通过控制各页面以一定顺序循环播放,就达到了轮播的效果。为此,我们可以使用Handler的sendEmptyMessageDelayed()方法来实现定时更新,并注意用户也可能会对带有轮播效果的ViewPager手动进行滑动操作,因此我认为用户这时候是希望查看指定页面的,这时候应该取消轮播。

以上所述是小编给大家介绍的Android 使用ViewPager实现左右循环滑动及轮播效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


推荐阅读
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 本文讨论了在Spring 3.1中,数据源未能自动连接到@Configuration类的错误原因,并提供了解决方法。作者发现了错误的原因,并在代码中手动定义了PersistenceAnnotationBeanPostProcessor。作者删除了该定义后,问题得到解决。此外,作者还指出了默认的PersistenceAnnotationBeanPostProcessor的注册方式,并提供了自定义该bean定义的方法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
author-avatar
眼泪--保持微笑_572_205
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有