0
点赞
收藏
分享

微信扫一扫

RecyclerView代替ViewPager实现Banner无限循环


为什么选用RecyclerView来实现图片轮播?

  • RecyclerView有优秀的缓存机制缓存相同Item
  • RecyclerView的Adapter可以针对不同Item的 ViewType来缓存不同的Item
  • RecyclerView已经自带的解决了与ViewPager, RecyclerView等滑动控件中的嵌套滑动冲突
  • RecyclerView自带是否预加载(即懒加载)功能,可以垂直方向,水平方向滑动,如各种很炫的效果

不选用ViewPager来实现图片轮播的理由?

  • 如果Banner嵌套在RecyclerView或ListView中, 那么ViewPager方式可能会出现动画卡顿和白屏,而RecyclerView则不会。

实例:

首先我们先自定义一个图片的指示:
在attrs.xml定义属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="PixIndicator">
<!-- 圆点的颜色 -->
<attr name="selectColor" format="color" />
<!-- 未选中圆点的颜色 -->
<attr name="unselectedColor" format="color" />
<!-- 圆点的大小 -->
<attr name="radius" format="dimension" />
<!-- 圆点间间距的大小 -->
<attr name="space" format="dimension" />
</declare-styleable>
</resources>

定义图片指示器类:

package com.jk.displaypix;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class PixIndicator extends View {
private int number;
private int position = 0;
private Paint paint = new Paint();
private int selectColor;
private int unselectColor;
private float radius;
private float space;

// 最新被执行
{
paint.setStyle(Paint.Style.FILL);
paint.setAntiAlias(true);
}

public PixIndicator(Context context) {
this(context,null);
}

public PixIndicator(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);

// 获取在布局文件中配置的属性值
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PixIndicator);
this.selectColor = typedArray.getColor(R.styleable.PixIndicator_selectColor, Color.RED);
this.unselectColor = typedArray.getColor(R.styleable.PixIndicator_unselectedColor, Color.BLACK);
this.radius = typedArray.getDimension(R.styleable.PixIndicator_radius, 10);
this.space = typedArray.getDimension(R.styleable.PixIndicator_space, 20);
typedArray.recycle();

}

// 图出所有点
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
float startPosition = ((float)getWidth()) / 2 - (radius * 2 * number + space * (number - 1)) / 2;
for (int i = 0; i < number; i++) {
if (i == position) {
paint.setColor(selectColor);
} else {
paint.setColor(unselectColor);
}
canvas.drawCircle(startPosition + radius * (2 * i + 1) + i * space, ((float)getHeight()) / 2, radius, paint);
}
canvas.restore();

}

// 设置图片数量
public void setSize(int number) {
this.number = number;
}

// 设置当前图片位置
public void setCurrentPosition(int position) {
this.position = position;
invalidate();
}

}

第一步设置好布局:

<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="200dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
/>
<com.jk.displaypix.PixIndicator
android:id="@+id/indicator"
android:layout_width="wrap_content"
android:layout_height="32dp"
app:selectColor="@color/colorAccent"
app:unselectedColor="#ffffff"
app:radius="3dp"
app:space="10dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="@id/recycler"/>

</androidx.constraintlayout.widget.ConstraintLayout>

第二步准备好图片:

List<Integer> list = new ArrayList<>(4);
// 存入图片
list.add(R.drawable.b1);
list.add(R.drawable.b2);
list.add(R.drawable.b3);
list.add(R.drawable.b4);

第三步准备好RecyclerView Adapter:

package com.jk.displaypix;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;

import java.util.List;

public class BannerAdapter extends RecyclerView.Adapter<BannerAdapter.ViewHolder> {

private List<Integer> list;
private Context context;

public BannerAdapter(Context context, List<Integer> list){
this.list=list;
this.context=context;
}

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

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Glide.with(context).load(list.get(position%list.size())).into(holder.imageView);
}

@Override
public int getItemCount() {
return Integer.MAX_VALUE;
}

class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;

public ViewHolder(View itemView) {
super(itemView);
imageView= itemView.findViewById(R.id.item_image);
}
}

}

第四步:配置RecyclerView

= new BannerAdapter(this, list);
RecyclerView recyclerView = findViewById(R.id.recycler);
// LinearLayoutManager 第二个参数表示布局方向,默认是垂直的
// 这里轮播广告要用为LinearLayoutManager.HORIZONTAL,水平方向
// 使用LinearLayoutManager会让图片滚动太快,我们继承LinearLayoutManager写一个子类重写它的滑动,让它不要太快
// LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
SmoothLinearLayoutManager layoutManager = new SmoothLinearLayoutManager(this, SmoothLinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
// 由于广告是一页一页的划过去,所以我们还需要用SnapHelper的子类PagerSnapHelper。直接追加到上面的recyclerView.setAdapter(adapter) 后面。
PagerSnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);
// recyclerView.scrollToPosition(list.size()*10)这句使RecyclerView一开始位于 list.size()*10 处,避免了一开始position为0不能前滑的尴尬
recyclerView.scrollToPosition(list.size() * 10);

第五步:实现自动播放

ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
// layoutManager.findFirstVisibleItemPosition() 表示得到当前RecyclerView第一个能看到的item的位置。
// 由于广告是每次展示一张,所以得到的就是当前图片的position。
// recyclerView.smoothScrollToPosition(int position)表示滑动到某个position。
// 所以上面的代码就表示每过2秒滑动到下个position,以此来完成自动轮播。
recyclerView.smoothScrollToPosition(layoutManager.findFirstVisibleItemPosition() + 1);
}
}, 2000, 2000, TimeUnit.MILLISECONDS);// 表示2秒后每过2秒运行一次run()里的程序

第六步:更新图片指示器

= findViewById(R.id.indicator);
bannerIndicator.setSize(list.size());
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
// onScrollStateChangedd的 newState 参数有三种状态:
// SCROLL_STATE_IDLE 静止状态
// SCROLL_STATE_DRAGGING 拖拽状态
// SCROLL_STATE_SETTLING手指离开后的惯性滚动状态
// 当RecyclerView的状态为SCROLL_STATE_IDLE时得到当前图片的position,然后与图片列表取余就得到指示器红点的位置。

if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int i = layoutManager.findFirstVisibleItemPosition() % list.size();
//得到指示器红点的位置
bannerIndicator.setCurrentPosition(i);
}
}
});

​​轮播图的Demo共享在GitHub了​​ 谢谢阅读


举报

相关推荐

0 条评论