【Android】Viewpager2实现无限轮播图
文章目录
- 【Android】Viewpager2实现无限轮播图
-
- 🏇先上效果图
- 🎍使用步骤
-
- [🏀step1 添加依赖](#🏀step1 添加依赖)
- [🍔step2 自定义RecyclerView.Adapter](#🍔step2 自定义RecyclerView.Adapter)
- [🚗step3 在页面中使用](#🚗step3 在页面中使用)
- 关键点分析
- 🍟如何自定义Indicator
- 🛌内置IndicatorView使用方法介绍,没有提供任何自定义属性
🏇先上效果图

这就类似于常用软件首页的广告轮播图,这里只是简单显示了几张图片,当然你还可以自定义更精美的子项布局来实现想要的效果。
🎍使用步骤
🏀step1 添加依赖
首先在app/build.gradle文件中添加依赖:
implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'me.relex:circleindicator:2.1.6'
🍔step2 自定义RecyclerView.Adapter
java
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
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_carousel, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.imageView.setImageResource(imageResources[position]);
}
@Override
public 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 在页面中使用
java
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; // 初始位置
@Override
protected 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() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
currentPage = position;
updateIndicators(position % images.size());
}
});
// 设置自动轮播
autoScrollRunnable = new Runnable() {
@Override
public 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;
});
}
@Override
protected void onResume() {
super.onResume();
startAutoScroll();
}
@Override
protected 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
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_banner, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
int realPosition = position % images.size();
holder.imageView.setImageResource(images.get(realPosition));
}
@Override
public 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);
}
}
}
}
关键点分析
无限循环原理
java
// 自动轮播系统
autoScrollRunnable = new Runnable() {
@Override
public 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);
}
};
java
@Override
public int getItemCount() {
return Integer.MAX_VALUE; // 实现无限循环
}
通过设置Integer的最大值(2147483646)达到无限循环效果,同时通过setCurrentItem(INITIAL_POSITION)
设定初始位置在索引等于1000的位置确保起始点在中间位置,每次轮播时通过position % images.size()
计算实际索引获取真实图片位置。
🍟如何自定义Indicator
java
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 |