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

作者:676598624 时间:2022-10-28 15:23:21 

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

循环滑动效果的实现:PagerAdapter

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


class ImageAdapter extends PagerAdapter {
private ArrayList<ImageView> viewlist;
public ImageAdapter(ArrayList<ImageView> 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:


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.viewpagerdemo.MainActivity" >
<android.support.v4.view.ViewPager
 android:id="@+id/main_viewpager"
 android:layout_width="match_parent"
 android:layout_height="150dp"
 android:layout_gravity="center" >
</android.support.v4.view.ViewPager>
</RelativeLayout>

代码操作MainActivity:


public class MainActivity extends Activity {
private static final String LOG_TAG = "MainActivity";
private ImageHandler handler = new ImageHandler(new WeakReference<MainActivity>(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<ImageView> views = new ArrayList<ImageView>();
 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<MainActivity> weakReference;
 private int currentItem = 0;
 protected ImageHandler(WeakReference<MainActivity> 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<MainActivity> weakReference;
 private int currentItem = 0;
 protected ImageHandler(WeakReference<MainActivity> 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实现左右循环滑动及轮播效果网站的支持!

来源:http://blog.csdn.net/qq_26761229/article/details/55194688

标签:viewpager,左右,滑动
0
投稿

猜你喜欢

  • Mybatis结果集自动映射的实例代码

    2023-07-09 02:13:58
  • C#如何Task执行任务,等待任务完成

    2022-03-06 11:31:31
  • spring boot使用logback日志级别打印控制操作

    2021-08-11 07:40:03
  • SpringBoot自动配置原理分析

    2022-03-02 17:42:51
  • c语言实现基数排序解析及代码示例

    2021-10-17 19:37:51
  • Android DrawLayout结合ListView用法实例

    2021-10-29 02:30:28
  • Springboot 整合RabbitMq(用心看完这一篇就够了)

    2023-11-23 05:27:17
  • Android实现点击获取验证码倒计时效果

    2022-08-29 09:23:41
  • Android 仿微信自定义数字键盘的实现代码

    2021-10-06 06:58:54
  • C#入门教程之集合ArrayList用法详解

    2022-04-30 06:32:13
  • java微信公众号企业付款开发

    2023-04-07 00:21:08
  • 详解Java中方法重写与重载的区别(面试高频问点)

    2022-07-19 10:36:48
  • Android性能优化之利用强大的LeakCanary检测内存泄漏及解决办法

    2021-07-26 11:57:46
  • SpringBoot拦截器的配置使用介绍

    2021-06-20 07:25:54
  • Redis在springboot中的使用教程

    2021-10-02 18:10:46
  • Android开发之imageView图片按比例缩放的实现方法

    2023-01-09 21:52:51
  • Java实现动态数字时钟

    2022-05-07 08:56:56
  • Java基础之Stream流原理与用法详解

    2021-06-07 22:16:05
  • SpringMVC实现数据绑定及表单标签

    2022-03-24 18:06:47
  • 深入HRESULT与Windows Error Codes的区别详解

    2023-10-16 15:19:42
  • asp之家 软件编程 m.aspxhome.com