自定义ViewPager实现图片自动轮播无限循环

自定义ViewPager实现图片自动轮播无限循环Viewpager 图片自动轮播无限循环是 Android 开发中经常用到的功能 功能实现起来也比较简单 虽然如此 但是很多情况下做出来的效果并不太让人满意 甚至有些上线的项目自动轮播上也会出现一些 bug 比如切换过程中出现空白页面 有些甚至在滑动过程中造成程序崩溃 本篇文章将实现 ViewPager 图片自动轮播无限循环 而且页面切换效果非常流畅 还是先看效果图 页面循环切换最容易出现问题的地方

Github 获取最新版本

Viewpager图片自动轮播无限循环是Android项目中经常用到的功能,功能实现起来也比较简单,但会出现不少问题。因此很多情况下做出来的效果并不太让人满意,甚至有些上线的项目自动轮播上也会出现一些bug。比如切换过程中出现空白页面,有些甚至在滑动过程中造成程序崩溃。本文内容对ViewPager进行封装,实现了可循环轮播的CirclSeViewPager, 该控件有较强的可扩展性,可接受任意类型的集合数据,可以自定义任意的轮播页面样式。页面切换也比较流畅。

接下来将通过以下几个小节对CircleViewPager做具体的分析。

  • 如何使用CircleViewPager
  • CircleViewPager的实现思路
  • CircleViewPager具体实现
  • CircleViewPager实现自动轮播
  • CircleViewPager页面点击事件

如果只是想实现无限轮播的效果那么只需看第一节即可,源码可在文章末尾下载。

 

2.在xml文件中添加如下代码:

<com.zhpan.viewpager.view.CircleViewPager android:id="@+id/viewpager2" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginEnd="35dp" android:layout_marginStart="35dp" app:interval="5000" /> 

3.CircleViewPager属性

 // 是否显示指示器 mViewpager.isShowIndicator(true); // 设置指示器位置 mViewpager.setIndicatorGravity(CircleViewPager.IndicatorGravity.END); // 设置指示器圆点半径 mViewpager.setIndicatorRadius(6); // 设置圆点指示器颜色  mViewPager.setIndicatorColor(getResources().getColor(R.color.colorAccent), getResources().getColor(R.color.colorPrimary)); // 设置是否无限循环 mViewpager.setCanLoop(true); // 设置是否自动轮播 mViewpager.setAutoPlay(true); // 设置图片切换时间间隔 mViewpager.setInterval(3000); // 设置页面点击事件 mViewpager.setOnPageClickListener(new CircleViewPager.OnPageClickListener() { 
    @Override public void onPageClick(int position) { 
    List<DataBean> list = mViewpager.getList(); Toast.makeText(MainActivity.this, "点击了" + list.get(position).getDescribe(), Toast.LENGTH_SHORT).show(); } }); // 设置数据 mViewpager.setPages(mList, new HolderCreator<ViewHolder>() { 
    @Override public ViewHolder createViewHolder() { 
    return new MyViewHolder(); } }); 

4.自定义ViewHolder

public class MyViewHolder implements ViewHolder<String> { 
    private ImageView mImageView; @Override public View createView(Context context) { 
    // 返回页面布局文件 View view = LayoutInflater.from(context).inflate(R.layout.banner_item, null); mImageView = (ImageView) view.findViewById(R.id.banner_image); return view; } @Override public void onBind(Context context, int position, String data) { 
    // 数据绑定 ImageLoaderUtil.loadImg(mImageView, (String) data); } } 

5.为防止内存泄露在onDestory()中停止图片轮播

 @Override protected void onDestroy() { 
    super.onDestroy(); mViewpager.stopLoop(); } 
<resources> <declare-styleable name="MyViewPager">  
    <attr name="lightDotRes" format="reference"/>  
    <attr name="darkDotRes" format="reference"/>  
    <attr name="dotWidth" format="dimension"/>  
    <attr name="interval" format="integer"/>  
     declare-styleable>  
      resources> 

2.新建CircleViewPager的布局文件view_pager_layout.xml,代码如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v4.view.ViewPager android:id="@+id/vp_main" android:layout_width="match_parent" android:layout_height="match_parent" /> <LinearLayout android:id="@+id/ll_main_dot" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignBottom="@id/vp_main" android:layout_marginBottom="10dp" android:gravity="center_horizontal" android:orientation="horizontal" />  
     RelativeLayout> 

3.定义CircleViewPager类并继承FrameLayout,并在构造方法中初始化数据,代码如下:

 public CircleViewPager(Context context) { 
    super(context); init(null); } public CircleViewPager(Context context, AttributeSet attrs) { 
    this(context, attrs, 0); init(attrs); } public CircleViewPager(Context context, AttributeSet attrs, int defStyleAttr) { 
    super(context, attrs, defStyleAttr); init(attrs); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
    super.onLayout(changed, left, top, right, bottom); if (changed) { 
    initData(); setIndicatorImage(); setViewPager(); setIndicatorLocation(); } } private void init(AttributeSet attrs) { 
    if (attrs != null) { 
    TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CircleViewPager); mLightIndicator = typedArray.getResourceId(R.styleable.CircleViewPager_lightDotRes, R.drawable.red_dot); mDarkIndicator = typedArray.getResourceId(R.styleable.CircleViewPager_darkDotRes, R.drawable.red_dot_night); mDotWidth = typedArray.getDimension(R.styleable.CircleViewPager_dotWidth, 20); interval = typedArray.getInteger(R.styleable.CircleViewPager_interval, 3000); typedArray.recycle(); } mView = LayoutInflater.from(getContext()).inflate(R.layout.view_pager_layout, this); mLlDot = (LinearLayout) mView.findViewById(R.id.ll_main_dot); mViewPager = (ViewPager) mView.findViewById(R.id.vp_main); mList = new ArrayList<>(); mListAdd = new ArrayList<>(); mIvDotList = new ArrayList<>(); } 

4.重写onLayout()方法,根据图片URL集合创建图片对应的ImageVIew和小圆点对应的ImageView.

 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 
    super.onLayout(changed, left, top, right, bottom); if (changed) { 
    initData(); setIndicatorImage(); setViewPager(); setIndicatorLocation(); } } // 根据mList数据集构造mListAdd private void initData() { 
    if (mList.size() == 0) { 
    mView.setVisibility(GONE); } else if (mList.size() == 1) { 
    mListAdd.add(mList.get(0)); } else if (mList.size() > 1) { 
    for (int i = 0; i < mList.size() + 2; i++) { 
    if (i == 0) { 
    // 判断当i=0为该处的mList的最后一个数据作为mListAdd的第一个数据 mListAdd.add(mList.get(mList.size() - 1)); } else if (i == mList.size() + 1) { 
    // 判断当i=mList.size()+1时将mList的第一个数据作为mListAdd的最后一个数据 mListAdd.add(mList.get(0)); } else { 
    // 其他情况 mListAdd.add(mList.get(i - 1)); } } } } // 设置轮播小圆点 private void setIndicatorImage() { 
    // 设置LinearLayout的子控件的宽高,这里单位是像素。 LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) mDotWidth, (int) mDotWidth); params.rightMargin = (int) (mDotWidth / 1.5); if (mList.size() > 1) { 
    // for循环创建mUrlList.size()个ImageView(小圆点) for (int i = 0; i < mList.size(); i++) { 
    ImageView imageViewDot = new ImageView(getContext()); imageViewDot.setLayoutParams(params); // 设置小圆点的背景为暗红图片 imageViewDot.setBackgroundResource(mDarkIndicator); mLlDot.addView(imageViewDot); mIvDotList.add(imageViewDot); } } //设置第一个小圆点图片背景为红色 if (mList.size() > 1) { 
    mIvDotList.get(dotPosition).setBackgroundResource(mLightIndicator); } } 

5.为ViewPager适配数据

 private void setViewPager() { 
    CirclePagerAdapter<T> adapter = new CirclePagerAdapter<>(mListAdd, this, holderCreator); mViewPager.setAdapter(adapter); mViewPager.setCurrentItem(currentPosition); setPageChangeListener(); startLoop(); setTouchListener(); if (showIndicator) { 
    mLlDot.setVisibility(VISIBLE); } else { 
    mLlDot.setVisibility(GONE); } } 

6.接下来为ViewPager添加页面改变的监听事件。

// ViewPager页面改变监听 private void setPageChangeListener() { 
    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { 
    @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 
    } @Override public void onPageSelected(int position) { 
    pageSelected(position); } @Override public void onPageScrollStateChanged(int state) { 
    // 当state为SCROLL_STATE_IDLE即没有滑动的状态时切换页面 if (state == ViewPager.SCROLL_STATE_IDLE) { 
    mViewPager.setCurrentItem(currentPosition, false); } } }); } private void pageSelected(int position) { 
    if (position == 0) { 
    //判断当切换到第0个页面时把currentPosition设置为list.size(),即倒数第二个位置,小圆点位置为length-1 currentPosition = mList.size(); dotPosition = mList.size() - 1; } else if (position == mList.size() + 1) { 
    //当切换到最后一个页面时currentPosition设置为第一个位置,小圆点位置为0 currentPosition = 1; dotPosition = 0; } else { 
    currentPosition = position; dotPosition = position - 1; } // 把之前的小圆点设置背景为暗红,当前小圆点设置为红色 mIvDotList.get(prePosition).setBackgroundResource(mDarkIndicator); mIvDotList.get(dotPosition).setBackgroundResource(mLightIndicator); prePosition = dotPosition; } 
 Handler mHandler = new Handler(); Runnable mRunnable = new Runnable() { 
    @Override public void run() { 
    if (mViewPager.getChildCount() > 1) { 
    mHandler.postDelayed(this, interval); currentPosition++; mViewPager.setCurrentItem(currentPosition, true); } } }; private void startLoop() { 
    if (!isLoop && mViewPager != null) { 
    mHandler.postDelayed(mRunnable, interval);// 每两秒执行一次runnable. isLoop = true; } } 

2.自动轮播实现后发现会有些问题,即在手动滑动页面时,页面仍然会自动切换。这样体验效果是非常不好的,因此我们需要在手动滑动时停止自动轮播,当手动滑动结束时再开启自动轮播。因此我们可以重写ViewPager的onTouch事件进行处理,当触发ACTION_DOWN和ACTION_MOVE时停止自动轮播,当触发ACTION_UP和ACTION_CANCEL时再开启自动轮播。代码实现如下:

// 设置触摸事件,当滑动或者触摸时停止自动轮播 private void setTouchListener() { 
    mViewPager.setOnTouchListener(new OnTouchListener() { 
    @Override public boolean onTouch(View v, MotionEvent event) { 
    int action = event.getAction(); switch (action) { 
    case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: isLoop = true; stopLoop(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: isLoop = false; startLoop(); default: break; } return false; } }); } private void startLoop() { 
    if (!isLoop && mViewPager != null) { 
    mHandler.postDelayed(mRunnable, interval);// 每interval秒执行一次runnable. isLoop = true; } } public void stopLoop() { 
    if (isLoop && mViewPager != null) { 
    mHandler.removeCallbacks(mRunnable); isLoop = false; } } 
 @Override public Object instantiateItem(final ViewGroup container, final int position) { 
    View view = getView(position, container); container.addView(view); return view; } // 根据图片URL创建对应的ImageView并添加到集合 private View getView(final int position, ViewGroup container) { 
    ViewHolder holder = holderCreator.createViewHolder(); if (holder == null) { 
    throw new RuntimeException("can not return a null holder"); } View view = holder.createView(container.getContext()); if (list != null && list.size() > 0) { 
    holder.onBind(container.getContext(), position, list.get(position)); } view.setOnClickListener(new View.OnClickListener() { 
    @Override public void onClick(View v) { 
    viewPager.imageClick(position - 1); } }); return view; } 
 private OnPageClickListener mOnPageClickListener; public void setOnPageClickListener(OnPageClickListener onPageClickListener) { 
    this.mOnPageClickListener = onPageClickListener; } // adapter中图片点击的回掉方法 public void imageClick(int position) { 
    mOnPageClickListener.pageClickListener(position); } // 监听页面点击的接口 public interface OnPageClickListener { 
    void pageClickListener(int position); } 

3.MainActivity中设置页面点击的监听。通过CicleViewPager.setOnPageClickListener实现对页面点击的监听。

mViewpager.setOnPageClickListener(new CircleViewPager.OnPageClickListener() { 
    @Override public void pageClickListener(int position) { 
    Toast.makeText(MainActivity.this, "点击了第"+position+"个美眉 \nURL:"+mViewpager.getUrlList().get(position), Toast.LENGTH_SHORT).show(); } }); 

Github 获取最新版本

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/203939.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月19日 下午9:15
下一篇 2026年3月19日 下午9:15


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号