【Android】ViewPager2实现手/自动轮播图

轮播图

轮播图的基本概念

定义:轮播图是一种可以水平或垂直滑动切换的多内容展示区域,通常自动循环播放。

实现

添加依赖

这里我们通过Glide加载图片,所以添加Glide的依赖

java 复制代码
  implementation 'com.github.bumptech.glide:glide:4.16.0'

布局

这里viewpager2就是我们进行轮播的对象,LinearLayout是我们添加指示器的布局(用于显示当前是第几张图片的下标指示器);

java 复制代码
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

     <androidx.viewpager2.widget.ViewPager2
         android:id = "@+id/vp2_main"
         android:layout_width="match_parent"
         android:layout_height="400dp"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toStartOf="parent">

     </androidx.viewpager2.widget.ViewPager2>
     <LinearLayout
         android:orientation="horizontal"
         android:id = "@+id/ll_main"
         app:layout_constraintStart_toStartOf="@+id/vp2_main"
         app:layout_constraintBottom_toBottomOf="@+id/vp2_main"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         app:layout_constraintEnd_toEndOf="@+id/vp2_main">

     </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Viewpager2创建布局

这里很简单,就是一个 ImageView

java 复制代码
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id = "@+id/iv_vp2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/newyifu"
        android:scaleType="centerCrop">

    </ImageView>
</androidx.constraintlayout.widget.ConstraintLayout>

Viewpager2适配器的创建

因为我们使用的是Glide加载图片,Gilde可以大大提高图片加载的性能,同时要异步操作;

java 复制代码
Glide.with(context).load(list.get(position)).into(holder.imageView);

这里with需要context,我们在onCreateViewHolder方法中可以通过父布局来获得contextload方法代表我们加载的资源,into代表我们要加载到哪里;

其他部分没变化;

java 复制代码
public class Viewpage2Adapter extends RecyclerView.Adapter<Viewpage2Adapter.viewhold> {
    List<Integer> list;
    Context context;
    public Viewpage2Adapter(List<Integer> list) {
        this.list = list;
    }

    @NonNull
    @Override
    public viewhold onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        context = parent.getContext();
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_vp2,parent,false);
        return new viewhold(view);
    }

    @Override
    public void onBindViewHolder(@NonNull viewhold holder, int position) {
        //使用glide加载可以优化流程,gilde加载属于异步
        Glide.with(context).load(list.get(position)).into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return list == null ? 0 : list.size();
    }

    class viewhold extends RecyclerView.ViewHolder{
        ImageView imageView;

        public viewhold(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.iv_vp2);
        }
    }
}

实现轮播效果

我们通过Carousel来实现轮播效果;

java 复制代码
public class Carousel {
    private Context context;//得到context
    private ViewPager2 mviewpage2;
    private LinearLayout mlinearLayout;//得到它用来设置指示器
    private List<ImageView> midtlist = new LinkedList<>();
    private List<Integer> midlist = new LinkedList<>();  //用于存放大图的Id,为了后续实现轮播效果
    private Long AUTO_SCROLL_INTERAEL = 1_500L;
    public Carousel(Context context, LinearLayout mlinearLayout, ViewPager2 mviewpage2) {
        this.context = context;
        this.mlinearLayout = mlinearLayout;
        this.mviewpage2 = mviewpage2;
    }

    public void init(List<Integer> idlist) {
        for (int i : idlist) {
            midlist.add(i);
            ImageView imageView = new ImageView(context);
            if (midlist.size()==1) {
                imageView.setImageResource(R.drawable.cheng);
            } else {
                imageView.setImageResource(R.drawable.grey);
            }
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(50, 50);
            layoutParams.setMargins(5, 0, 5, 0);
            imageView.setLayoutParams(layoutParams);
            midtlist.add(imageView);
            mlinearLayout.addView(imageView);

        }
        midlist.add(0, midlist.get(midlist.size()  - 1));
        midlist.add(midlist.get(1));
        Log.d("gk",""+midlist.get(0));
        Viewpage2Adapter viewpage2Adapter = new Viewpage2Adapter(midlist);
        mviewpage2.setAdapter(viewpage2Adapter);
        mviewpage2.setCurrentItem(1, false);

            //实现轮播
            mviewpage2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
                @Override
                public void onPageSelected(int position) {
                    for (int i1 = 0; i1 < midtlist.size(); i1++) {
                        if (i1 == position - 1) {
                            midtlist.get(i1).setImageResource(R.drawable.cheng);
                        } else {
                            midtlist.get(i1).setImageResource(R.drawable.grey);
                        }
                    }
                    if (position == midlist.size() - 1) {
                        mviewpage2.setCurrentItem(1, false);

                    }
                    if (position == 0) {
                        mviewpage2.setCurrentItem(midlist.size() - 2, false);
                    }
                    super.onPageSelected(position);
                }
            });

    }
    };
}

我们来分解一下代码:

java 复制代码
  private Context context;//得到context
    private ViewPager2 mviewpage2;
    private LinearLayout mlinearLayout;//得到它用来设置指示器
    private List<ImageView> midtlist = new LinkedList<>();
    private List<Integer> midlist = new LinkedList<>();  //用于存放大图的Id,为了后续实现轮播效果
    private Long AUTO_SCROLL_INTERAEL = 1_500L;
   public Carousel(Context context, LinearLayout mlinearLayout, ViewPager2 mviewpage2) {
        this.context = context;
        this.mlinearLayout = mlinearLayout;
        this.mviewpage2 = mviewpage2;
    }

context是为了后面newImageView的对象; mlinearLayout是为了设置指示器; midtlist是为了后面更新指示器的操作; 因为轮播的图片会有变动,所以我们需要保存变动后的图片资源,记为midlistAUTO_SCROLL_INTERAEL是轮播的时间;

java 复制代码
  for (int i : idlist) {
            midlist.add(i);             //把资源加到新的保存资源的List中;
            ImageView imageView = new ImageView(context);  //创建对应的指示器
            if (midlist.size()==1) {     //初始化,指向首位
                imageView.setImageResource(R.drawable.cheng);
            } else {
                imageView.setImageResource(R.drawable.grey);
            }
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(50, 50);   //设置这个指示的宽高
            layoutParams.setMargins(5, 0, 5, 0);   //设置指示器的边距
            imageView.setLayoutParams(layoutParams);   //绑定布局参数
            midtlist.add(imageView);        //添加到list中,方便后续更新
            mlinearLayout.addView(imageView);  //添加到布局中

        }

这段代码的作用是设置指示器(图片的下标);

java 复制代码
        //把最后一张资源设置到第一张
        midlist.add(0, midlist.get(midlist.size()  - 1));
        //把第一张资源设置到最后一张
        midlist.add(midlist.get(1));
        Log.d("gk",""+midlist.get(0));
        Viewpage2Adapter viewpage2Adapter = new Viewpage2Adapter(midlist);
        mviewpage2.setAdapter(viewpage2Adapter);
        //初始化到轮播图的第一张;
        mviewpage2.setCurrentItem(1, false);

这段代码的作用是初始化;把增加midlist的页数就是为了轮播时的切换;比如轮播到最后一页时,滑到下一页就是第一页,这是我们也偷偷切换到真的第一张,就能避免生硬的切换,使得看起来的效果更逼真;

java 复制代码
   mviewpage2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
                @Override
                public void onPageSelected(int position) {
                    for (int i1 = 0; i1 < midtlist.size(); i1++) {
                        if (i1 == position - 1) {
                            midtlist.get(i1).setImageResource(R.drawable.cheng);
                        } else {
                            midtlist.get(i1).setImageResource(R.drawable.grey);
                        }
                    }
                    if (position == midlist.size() - 1) {
                        mviewpage2.setCurrentItem(1, false);

                    }
                    if (position == 0) {
                        mviewpage2.setCurrentItem(midlist.size() - 2, false);
                    }
                    super.onPageSelected(position);
                }
            });

这里的效果是手动轮播;我们自行观看代码理解就OK; 想解释的一点是:i1 == position - 1这个条件,因为我们的轮播的页面多了,但是指示器还是原来的;所以这里我们这样设置就能避免指示器更新混乱的问题了; position == midlist.size() - 1就是当它是第一页的内容的时候,我们切换为实际第一页的内容,这样看起来就很比较丝滑不生硬;

自动轮播

java 复制代码
    Handler handler = new Handler(Looper.getMainLooper());
//得到handler的对象
   private final Runnable anToScrollRunnable = new Runnable() {
        @Override
        public void run() {

                int currentitem = mviewpage2.getCurrentItem();
                if (currentitem == midlist.size() - 2) {
                    mviewpage2.setCurrentItem(1,false);
                } else {
                    mviewpage2.setCurrentItem(currentitem + 1);
                }
                handler.postDelayed(anToScrollRunnable, AUTO_SCROLL_INTERAEL);
            }


    };

这段代码如果理解了手动的话,自动也不难理解; 然后我们在手势变换的回调中进行设置:

java 复制代码
          @Override
                public void onPageScrollStateChanged(int state) {
                    if (state == ViewPager2.SCROLL_STATE_DRAGGING) {
                        handler.removeCallbacks(anToScrollRunnable);
                    } else if (state == ViewPager2.SCROLL_STATE_IDLE) {
                        handler.removeCallbacks(anToScrollRunnable);
                        handler.postDelayed(anToScrollRunnable, AUTO_SCROLL_INTERAEL);
                    }
                    super.onPageScrollStateChanged(state);
                }

handler.removeCallbacks(anToScrollRunnable); 就是在任务队列中,移除还没有开始执行的任务

handler.postDelayed(anToScrollRunnable, AUTO_SCROLL_INTERAEL); 就是延迟执行任务,第一个参数是任务,第二个参数是延迟的时间

那么为什么需要移除,因为你如果没有取消"预约"的任务,一直堆积,那么最后的效果可能是,1.5秒切3次等等;

handler.removeCallbacks(anToScrollRunnable);就是停止自动轮播

handler.postDelayed(anToScrollRunnable, AUTO_SCROLL_INTERAEL);就是开始自动轮播

大家可以根据自己的理解自己设置;

我实现的效果:

最后方便大家学习,贴上综合代码:

java 复制代码
public class Carousel {
    private Context context;//得到context
    private ViewPager2 mviewpage2;
    private LinearLayout mlinearLayout;//得到它用来设置指示器
    private List<ImageView> midtlist = new LinkedList<>();
    private List<Integer> midlist = new LinkedList<>();  //用于存放大图的Id,为了后续实现轮播效果

    private boolean AUTO_SCROLL = false;
    private Long AUTO_SCROLL_INTERAEL = 1_500L;

    Handler handler = new Handler(Looper.getMainLooper());

    public Carousel(Context context, LinearLayout mlinearLayout, ViewPager2 mviewpage2) {
        this.context = context;
        this.mlinearLayout = mlinearLayout;
        this.mviewpage2 = mviewpage2;
    }

    public void init(List<Integer> idlist) {
        for (int i : idlist) {
            midlist.add(i);
            ImageView imageView = new ImageView(context);
            if (midlist.size()==1) {
                imageView.setImageResource(R.drawable.cheng);
            } else {
                imageView.setImageResource(R.drawable.grey);
            }
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(50, 50);
            layoutParams.setMargins(5, 0, 5, 0);
            imageView.setLayoutParams(layoutParams);
            midtlist.add(imageView);
            mlinearLayout.addView(imageView);

        }
        midlist.add(0, midlist.get(midlist.size()  - 1));
        midlist.add(midlist.get(1));
        Log.d("gk",""+midlist.get(0));
        Viewpage2Adapter viewpage2Adapter = new Viewpage2Adapter(midlist);
        mviewpage2.setAdapter(viewpage2Adapter);
        mviewpage2.setCurrentItem(1, false);

            //实现轮播
            mviewpage2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {

                @Override
                public void onPageScrollStateChanged(int state) {
                    if (state == ViewPager2.SCROLL_STATE_DRAGGING) {
                        handler.removeCallbacks(anToScrollRunnable);
                    } else if (state == ViewPager2.SCROLL_STATE_IDLE) {
                        handler.removeCallbacks(anToScrollRunnable);
                        handler.postDelayed(anToScrollRunnable, AUTO_SCROLL_INTERAEL);
                    }

                    super.onPageScrollStateChanged(state);
                }

                @Override
                public void onPageSelected(int position) {
                    for (int i1 = 0; i1 < midtlist.size(); i1++) {
                        if (i1 == position - 1) {
                            midtlist.get(i1).setImageResource(R.drawable.cheng);
                        } else {
                            midtlist.get(i1).setImageResource(R.drawable.grey);
                        }
                    }
                    if (position == midlist.size() - 1) {
                        mviewpage2.setCurrentItem(1, false);

                    }
                    if (position == 0) {
                        mviewpage2.setCurrentItem(midlist.size() - 2, false);
                    }
                    super.onPageSelected(position);
                }
            });

    }

    private final Runnable anToScrollRunnable = new Runnable() {
        @Override
        public void run() {

                int currentitem = mviewpage2.getCurrentItem();


                if (currentitem == midlist.size() - 2) {
                    mviewpage2.setCurrentItem(1,false);
                } else {
                    mviewpage2.setCurrentItem(currentitem + 1);

                }
                handler.postDelayed(anToScrollRunnable, AUTO_SCROLL_INTERAEL);
            }


    };
}
public class Viewpage2Adapter extends RecyclerView.Adapter<Viewpage2Adapter.viewhold> {
    List<Integer> list;
    Context context;
    public Viewpage2Adapter(List<Integer> list) {
        this.list = list;
    }

    @NonNull
    @Override
    public viewhold onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        context = parent.getContext();
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_vp2,parent,false);
        return new viewhold(view);
    }

    @Override
    public void onBindViewHolder(@NonNull viewhold holder, int position) {
        //使用glide加载可以优化流程,gilde加载属于异步
        Glide.with(context).load(list.get(position)).into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return list == null ? 0 : list.size();
    }

    class viewhold extends RecyclerView.ViewHolder{
        ImageView imageView;

        public viewhold(@NonNull View itemView) {
            super(itemView);
            imageView = itemView.findViewById(R.id.iv_vp2);
        }
    }
}
相关推荐
红尘散仙4 天前
TRNovel王者归来:让小说阅读"声"临其境的终端神器
前端·rust·ui kit
齐生6 天前
iOS - UIViewController 生命周期
ui kit
JordanHaidee1 个月前
【Rust GUI开发入门】编写一个本地音乐播放器(11. 支持动态明暗主题切换)
前端·ui kit
songgeb3 个月前
UIScene in iOS
ios·ui kit
大熊猫侯佩5 个月前
UIKit 在 UICollectionView 中拖放交换 Cell 视图的极简实现
swift·apple·ui kit
仿生狮子7 个月前
Reka UI 是个啥?
vue.js·nuxt.js·ui kit
Sinyu10127 个月前
Flutter 自定义 CustomPaint 实现流体液态加载动画
前端·flutter·ui kit
全宝8 个月前
🔥一个有质感的拟态开关
前端·css·weui