热门标签 | 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应用中实现动态滚动文本显示控件的优化方法。通过详细分析焦点管理机制,特别是通过设置返回值为`true`来确保焦点不会被其他控件抢占,从而提升滚动文本的流畅性和用户体验。具体实现中,对`MarqueeText.java`进行了代码层面的优化,增强了控件的稳定性和兼容性。 ... [详细]
  • 当前,众多初创企业对全栈工程师的需求日益增长,但市场中却存在大量所谓的“伪全栈工程师”,尤其是那些仅掌握了Node.js技能的前端开发人员。本文旨在深入探讨全栈工程师在现代技术生态中的真实角色与价值,澄清对这一角色的误解,并强调真正的全栈工程师应具备全面的技术栈和综合解决问题的能力。 ... [详细]
  • 深入解析Gradle中的Project核心组件
    在Gradle构建系统中,`Project` 是一个核心组件,扮演着至关重要的角色。通过使用 `./gradlew projects` 命令,可以清晰地列出当前项目结构中包含的所有子项目,这有助于开发者更好地理解和管理复杂的多模块项目。此外,`Project` 对象还提供了丰富的配置选项和生命周期管理功能,使得构建过程更加灵活高效。 ... [详细]
  • 为了优化直播应用底部聊天框的弹出机制,确保在不同设备上的布局稳定性和兼容性,特别是在配备虚拟按键的设备上,我们对用户交互流程进行了调整。首次打开应用时,需先点击首个输入框以准确获取键盘高度,避免直接点击第二个输入框导致的整体布局挤压问题。此优化通过调整 `activity_main.xml` 布局文件实现,确保了更好的用户体验和界面适配。 ... [详细]
  • 在Spring框架中,基于Schema的异常通知与环绕通知的实现方法具有重要的实践价值。首先,对于异常通知,需要创建一个实现ThrowsAdvice接口的通知类。尽管ThrowsAdvice接口本身不包含任何方法,但开发者需自定义方法来处理异常情况。此外,环绕通知则通过实现MethodInterceptor接口来实现,允许在方法调用前后执行特定逻辑,从而增强功能或进行必要的控制。这两种通知机制的结合使用,能够有效提升应用程序的健壮性和灵活性。 ... [详细]
  • SQLmap自动化注入工具命令详解(第28-29天 实战演练)
    SQL注入工具如SQLMap等在网络安全测试中广泛应用。SQLMap是一款开源的自动化SQL注入工具,支持12种不同的数据库,具体支持的数据库类型可在其插件目录中查看。作为当前最强大的注入工具之一,SQLMap在实际应用中具有极高的效率和准确性。 ... [详细]
  • 深入解析 Android Drawable:第六阶段进阶指南 ... [详细]
  • C#编程指南:实现列表与WPF数据网格的高效绑定方法 ... [详细]
  • 深入解析Tomcat:开发者的实用指南
    深入解析Tomcat:开发者的实用指南 ... [详细]
  • 如何在Java中高效构建WebService
    本文介绍了如何利用XFire框架在Java中高效构建WebService。XFire是一个轻量级、高性能的Java SOAP框架,能够简化WebService的开发流程。通过结合MyEclipse集成开发环境,开发者可以更便捷地进行项目配置和代码编写,从而提高开发效率。此外,文章还详细探讨了XFire的关键特性和最佳实践,为读者提供了实用的参考。 ... [详细]
  • 在处理大规模并发请求时,传统的多线程或多进程模型往往无法有效解决性能瓶颈问题。尽管它们在处理小规模任务时能提升效率,但在高并发场景下,系统资源的过度消耗和上下文切换的开销会显著降低整体性能。相比之下,Python 的 `asyncio` 模块通过协程提供了一种轻量级且高效的并发解决方案。本文将深入解析 `asyncio` 模块的原理及其在实际应用中的优化技巧,帮助开发者更好地利用协程技术提升程序性能。 ... [详细]
  • 可转债数据智能抓取与分析平台优化
    本项目旨在优化可转债数据的智能抓取与分析平台。通过爬取集思录上的可转债信息(排除已发布赎回的债券),并结合安道全教授提出的三条安全线投资策略,新增了建仓线、加仓线和重仓线,以提供更精准的投资建议。 ... [详细]
  • 如何构建基于Spring MVC框架的Java Web应用项目
    在构建基于Spring MVC框架的Java Web应用项目时,首先应创建一个新的动态Web项目。接着,需将必要的JAR包导入至WebContent/WEB-INF/lib目录下,确保包括Spring核心库及相关依赖。如遇缺失的JAR包,可向社区求助或通过Maven等工具自动下载。正确配置后,即可开始搭建应用结构与功能模块。 ... [详细]
  • 本文深入探讨了 HTML 中的 `margin` 属性,详细解析了其基本特性和应用场景。文章不仅介绍了 `margin` 的基本概念,还重点讨论了垂直外边距合并现象,并分析了 `margin` 在块级元素与内联元素中的不同表现。通过实例和代码示例,帮助读者全面理解 `margin` 的使用技巧和常见问题。 ... [详细]
  • 深入解析Spring Boot自动配置机制及其核心原理
    Spring Boot 的自动配置机制是其核心特性之一,旨在简化开发过程并提高效率。本文将深入探讨这一机制的工作原理,解释其如何通过智能化的类路径扫描和条件注解实现自动装配。通过对 Spring Boot 自动配置的详细解析,读者将能够更好地理解和应用这一强大功能,从而在实际项目中更加高效地利用 Spring Boot。 ... [详细]
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社区 版权所有