热门标签 | 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 图像色彩处理技术详解
    本文详细探讨了 Android 平台上的图像色彩处理技术,重点介绍了如何通过模仿美图秀秀的交互方式,利用 SeekBar 实现对图片颜色的精细调整。文章展示了具体的布局设计和代码实现,帮助开发者更好地理解和应用图像处理技术。 ... [详细]
  • 本文探讨了在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在实际应用中具有极高的效率和准确性。 ... [详细]
  • 探究Oracle数据库字符集编码的详细方法与实践
    本文深入探讨了Oracle数据库字符集编码的详细方法与实践。首先,通过执行 `SELECT USERENV('language') FROM DUAL;` 查询服务端字符集编码。其次,通过在注册表中搜索 `NLS_LANG` 参数来查看客户端字符集编码。此外,文章还介绍了如何在不同场景下正确配置和转换字符集,以确保数据的一致性和完整性。 ... [详细]
  • 本文深入探讨了在Android应用开发中常见的相机连接故障问题,特别是在RK3288平台和Android 6.0系统上。通过分析具体案例,本文提供了详细的解决方案和应对策略,旨在帮助开发者有效解决相机连接问题,提升应用的稳定性和用户体验。 ... [详细]
  • 如何利用Python脚本实现钉钉自动化考勤打卡
    本文详细探讨了利用Python脚本实现钉钉自动化考勤打卡的方法。通过具体实例操作,解决了很多用户在实际应用中遇到的难题,帮助读者高效掌握这一实用技能。 ... [详细]
  • 深入解析Spring Boot自动配置机制及其核心原理
    Spring Boot 的自动配置机制是其核心特性之一,旨在简化开发过程并提高效率。本文将深入探讨这一机制的工作原理,解释其如何通过智能化的类路径扫描和条件注解实现自动装配。通过对 Spring Boot 自动配置的详细解析,读者将能够更好地理解和应用这一强大功能,从而在实际项目中更加高效地利用 Spring Boot。 ... [详细]
  • 从 Java 过渡到 Ruby,不仅是一次编程语言的转换,更是一段技术进阶的旅程。本文将深入探讨两种语言在语法、生态系统和开发模式上的差异,帮助开发者顺利实现转型,并在新的环境中高效地编写高质量代码。 ... [详细]
  • 探讨Java中利用CXF与JAXRS实现Web服务请求及会话管理的常见问题与解决方案 ... [详细]
  • voc生成xml 代码
    目录 lxmlwindows安装 读取示例 可视化 生成示例 上面是代码,下面有调用示例 api调用代码,其实只有几行:这个生成代码也很简 ... [详细]
  • 如何利用Apache与Nginx高效实现动静态内容分离
    如何利用Apache与Nginx高效实现动静态内容分离 ... [详细]
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社区 版权所有