【Android】Viewpager2实现无限轮播图
【Android】Viewpager2实现无限轮播图
文章目录
- 【Android】Viewpager2实现无限轮播图
- 🏇先上效果图
- 🎍使用步骤
- 🏀step1 添加依赖
- 🍔step2 自定义RecyclerView.Adapter
- 🚗step3 在页面中使用
- 关键点分析
- 无限循环原理
- 🍟如何自定义Indicator
- 🛌内置IndicatorView使用方法介绍,没有提供任何自定义属性
🏇先上效果图
这就类似于常用软件首页的广告轮播图,这里只是简单显示了几张图片,当然你还可以自定义更精美的子项布局来实现想要的效果。
🎍使用步骤
🏀step1 添加依赖
首先在app/build.gradle文件中添加依赖:
implementation 'androidx.viewpager2:viewpager2:1.0.0'implementation 'me.relex:circleindicator:2.1.6'
🍔step2 自定义RecyclerView.Adapter
package com.example.viewpager2test;import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;public class CarouselAdapter extends RecyclerView.Adapter<CarouselAdapter.ViewHolder> {private final int[] imageResources; // 图片资源数组public CarouselAdapter(int[] imageResources) {this.imageResources = imageResources;}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_carousel, parent, false);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {holder.imageView.setImageResource(imageResources[position]);}@Overridepublic int getItemCount() {return imageResources.length;}static class ViewHolder extends RecyclerView.ViewHolder {ImageView imageView;ViewHolder(View view) {super(view);imageView = view.findViewById(R.id.imageView);}}
}
其实Viewpager2的底层是基于RecyclerView实现的,所以用法基本上差不多,都是要自定义适配器和子项布局,在适配器中实现布局加载,控件的初始化和数据绑定等操作。
🚗step3 在页面中使用
package com.example.viewpager2test;import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager2.widget.ViewPager2;
import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {private ViewPager2 viewPager;private LinearLayout indicatorLayout;private BannerAdapter adapter;private Handler handler = new Handler(Looper.getMainLooper());private Runnable autoScrollRunnable;private int currentPage = 0;private static final long AUTO_SCROLL_DELAY = 2000; // 3秒轮播间隔private static final int INITIAL_POSITION = 1000; // 初始位置@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);EdgeToEdge.enable(this);// 初始化视图viewPager = findViewById(R.id.viewPager);indicatorLayout = findViewById(R.id.indicatorLayout);// 创建图片资源列表List<Integer> images = new ArrayList<>();images.add(R.drawable.image1);images.add(R.drawable.image2);images.add(R.drawable.image3);images.add(R.drawable.image4);// 设置适配器adapter = new BannerAdapter(images);viewPager.setAdapter(adapter);// 设置初始位置(实现无限循环)viewPager.setCurrentItem(INITIAL_POSITION, false);currentPage = INITIAL_POSITION;// 添加指示器setupIndicators(images.size());// 设置页面变化监听器viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {@Overridepublic void onPageSelected(int position) {super.onPageSelected(position);currentPage = position;updateIndicators(position % images.size());}});// 设置自动轮播autoScrollRunnable = new Runnable() {@Overridepublic void run() {if (currentPage == adapter.getItemCount() - 1) {currentPage = INITIAL_POSITION;viewPager.setCurrentItem(currentPage, false);} else {viewPager.setCurrentItem(++currentPage, true);}handler.postDelayed(this, AUTO_SCROLL_DELAY);}};// 触摸暂停功能viewPager.setOnTouchListener((v, event) -> {stopAutoScroll();return false;});}@Overrideprotected void onResume() {super.onResume();startAutoScroll();}@Overrideprotected void onPause() {super.onPause();stopAutoScroll();}private void setupIndicators(int count) {indicatorLayout.removeAllViews();for (int i = 0; i < count; i++) {ImageView indicator = new ImageView(this);indicator.setImageResource(i == 0 ? R.drawable.indicator_active : R.drawable.indicator_inactive);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpToPx(8), dpToPx(8));params.setMargins(dpToPx(4), 0, dpToPx(4), 0);indicator.setLayoutParams(params);indicatorLayout.addView(indicator);}}private void updateIndicators(int position) {for (int i = 0; i < indicatorLayout.getChildCount(); i++) {ImageView indicator = (ImageView) indicatorLayout.getChildAt(i);indicator.setImageResource(i == position ?R.drawable.indicator_active : R.drawable.indicator_inactive);}}private void startAutoScroll() {handler.postDelayed(autoScrollRunnable, AUTO_SCROLL_DELAY);}private void stopAutoScroll() {handler.removeCallbacks(autoScrollRunnable);}private int dpToPx(int dp) {float density = getResources().getDisplayMetrics().density;return Math.round(dp * density);}// 适配器类static class BannerAdapter extends RecyclerView.Adapter<BannerAdapter.ViewHolder> {private final List<Integer> images;public BannerAdapter(List<Integer> images) {this.images = images;}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_banner, parent, false);return new ViewHolder(view);}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {int realPosition = position % images.size();holder.imageView.setImageResource(images.get(realPosition));}@Overridepublic int getItemCount() {return Integer.MAX_VALUE; // 实现无限循环}static class ViewHolder extends RecyclerView.ViewHolder {ImageView imageView;public ViewHolder(@NonNull View itemView) {super(itemView);imageView = itemView.findViewById(R.id.imageView);}}}
}
关键点分析
无限循环原理
// 自动轮播系统
autoScrollRunnable = new Runnable() {@Overridepublic void run() {if (currentPage == adapter.getItemCount() - 1) {currentPage = INITIAL_POSITION;viewPager.setCurrentItem(currentPage, false); // 无动画跳转} else {viewPager.setCurrentItem(++currentPage, true); // 平滑滚动}handler.postDelayed(this, AUTO_SCROLL_DELAY);}
};
@Overridepublic int getItemCount() {return Integer.MAX_VALUE; // 实现无限循环}
通过设置Integer的最大值(2147483646)达到无限循环效果,同时通过setCurrentItem(INITIAL_POSITION)
设定初始位置在索引等于1000的位置确保起始点在中间位置,每次轮播时通过position % images.size()
计算实际索引获取真实图片位置。
🍟如何自定义Indicator
private void setupIndicators(int count) {indicatorLayout.removeAllViews();for (int i = 0; i < count; i++) {ImageView indicator = new ImageView(this);indicator.setImageResource(i == 0 ? R.drawable.indicator_active : R.drawable.indicator_inactive);LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(dpToPx(8), dpToPx(8));params.setMargins(dpToPx(4), 0, dpToPx(4), 0);indicator.setLayoutParams(params);indicatorLayout.addView(indicator);}}private void updateIndicators(int position) {for (int i = 0; i < indicatorLayout.getChildCount(); i++) {ImageView indicator = (ImageView) indicatorLayout.getChildAt(i);indicator.setImageResource(i == position ?R.drawable.indicator_active : R.drawable.indicator_inactive);}}
🛌内置IndicatorView使用方法介绍,没有提供任何自定义属性
方法名 | 描述 |
---|---|
setIndicatorRadius(float indicatorRadius) | 设置圆点半径 |
setIndicatorSpacing(float indicatorSpacing) | 设置圆点间距 |
setIndicatorStyle(@IndicatorStyle int indicatorStyle) | 设置圆点切换动画,内置五种切换动画 |
setIndicatorColor(@ColorInt int indicatorColor) | 设置默认的圆点颜色 |
setIndicatorSelectorColor(@ColorInt int indicatorSelectorColor) | 设置选中的圆点颜色 |
setParams(RelativeLayout.LayoutParams params) | 设置IndicatorView在banner中的位置,默认底部居中,距离底部10dp |
setIndicatorRatio(float indicatorRatio) | 设置indicator比例,拉伸圆为矩形,设置越大,拉伸越长,默认1.0 |
setIndicatorSelectedRadius(float indicatorSelectedRadius) | 设置选中的圆角,默认和indicatorRadius值一致,可单独设置选中的点大小 |
setIndicatorSelectedRatio(float indicatorSelectedRatio) | 设置选中圆比例,拉伸圆为矩形,控制该比例,默认比例和indicatorRatio一致,默认值1.0 |
atorRatio(float indicatorRatio) | 设置indicator比例,拉伸圆为矩形,设置越大,拉伸越长,默认1.0 |
setIndicatorSelectedRadius(float indicatorSelectedRadius) | 设置选中的圆角,默认和indicatorRadius值一致,可单独设置选中的点大小 |
setIndicatorSelectedRatio(float indicatorSelectedRatio) | 设置选中圆比例,拉伸圆为矩形,控制该比例,默认比例和indicatorRatio一致,默认值1.0 |