依赖
在项目的gradle文件中引入一下这个包
implementation 'androidx.recyclerview:recyclerview:1.2.0'
基本使用
Activity
中代码
//首先要找到该组件(必做)
RecyclerView recyclerView = findViewById(R.id.recycleView);
//准备要展示的数据源(必做)
photoBeanList =PhotoUtil.getPhotosFromFiles(MainActivity.this);
//设置LayoutManager(必做),这里设置网格布局
//设置 layoutManager 这个是Recycleview必须的 不然Recycleview 的内容不会显示。
//RecyclView提供了 LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager 3 个系统的。当然也可以继承LayoutManager自定义
recyclerView.setLayoutManager(newGridLayoutManager(MainActivity.this, 3));
//下面是线性 layoutManager的用法,后面还会详细介绍
//LinearLayoutManager layoutManager = new //LinearLayoutManager(this);
//如果将 orientation 设置为 HORIZONTAL 可以轻易的实现横向的 listview 效果 //layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
//recyclerView.setLayoutManager(layoutManager);
//创建适配器对象(必做)
adapter = new PhotoAdapter(MainActivity.this, photoBeanList);
//设置适配器(必做)
recyclerView.setAdapter(adapter);
//设置动画(可选,在增加或删除条目时的动画)
recyclerView.setItemAnimator(new DefaultItemAnimator());
//使用自带的分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL));
//自定义分割(可选,需要继承RecyclerView.ItemDecoration类)
recyclerView.addItemDecoration(new mydivider());
Adapter
对应代码
基本的步骤为:
-
为
RecyclerView
新增适配器PhotoAdapter
,并让其继承于RecyclerView.Adapter
,把泛型指定为PhotoAdapter .ViewHolder
。
定义内部类ViewHolder,并继承RecyclerView.ViewHolder。传入的View参数通常是RecyclerView子项的最外层布局。 -
PhotoAdapter
构造函数,用于把要展示的数据源传入,并赋予值给全局变量dataList
。 -
PhotoAdapter
继承RecyclerView.Adapter
。因为必须重写onCreateViewHolder()
,onBindViewHolder()
和getItemCount()
三个方法
onCreateViewHolder()
用于创建ViewHolder
实例,并把加载的布局传入到构造函数去,再把ViewHolder
实例返回。onBindViewHolder()
则是用于对子项的数据进行赋值,会在每个子项被滚动到屏幕内时执行。position
得到当前项的photo
实例。getItemCount()
返回RecyclerView
的子项数目。
/**
* @ClassName PhotoAdapter
* @Description 图片数据适配器
* @Author Yu
* @Date 2022/6/17 10:53
* @Version 1.0
**/
//此处 继承的 RecyclerView.Adapter 中的泛型指定为我们自定义的 ViewHolder的类型(static class ViewHolder extends RecyclerView.ViewHolder)
public class PhotoAdapter extends RecyclerView.Adapter<PhotoAdapter.ViewHolder>{
private List<PhotoBean> dataList;
private BaseActivity activity;
private PhotoBean bean;
private int selectedIndex=-1;//用于记录当前选中的索引
public void setSelectedIndex(int position) {
this.selectedIndex = position;
}
public PhotoAdapter (BaseActivity activity,List<PhotoBean> dataList){
this.dataList=dataList;
this.activity=activity;
}
/** 创建 viewHolder 将 xml 传递给ViewHolder */
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.photo_item, parent, false);
ViewHolder holder = new ViewHolder(view);
holder.item_photo.setOnLongClickListener(v->{
int position = holder.getAdapterPosition();
setSelectedIndex(position);//声明并赋值一个当前选中的索引
AlertDialog.Builder builder = new AlertDialog.Builder(activity);//dialog
builder.setTitle("温馨提示");
builder.setMessage("真的要删除吗?删除后不可恢复");
builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
//确定对应的回执事件
@Override
public void onClick(DialogInterface dialog, int which) {
int i = PhotoUtil.deleteFromFile(activity, dataList.get(selectedIndex).getpId());//从数据库中删除
if (i>0){
Toast.makeText(activity, "删除成功!", Toast.LENGTH_SHORT).show();
dataList.remove(selectedIndex);//从列表中删除
notifyItemRemoved(selectedIndex);
//refreshList(dataList);//刷新视图
}else{
Toast.makeText(activity, "删除失败!", Toast.LENGTH_SHORT).show();
}
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
builder.create().show();
return true;
});
return holder;
}
/** 这里是设置item操作的地方 */
@Override
public void onBindViewHolder(@NonNull PhotoAdapter.ViewHolder holder, int position) {
bean=dataList.get(position);
Glide.with(activity)
.load(bean.getUrl())
.error(R.drawable.picloaderror)
.centerCrop()
.into(holder.item_photo);
holder.item_photo.setOnClickListener(v->{
//点击任意一张图片对应事件
Intent intent = new Intent(activity, PhotoViewPagesActivity.class);
intent.putParcelableArrayListExtra("DATALIST",(ArrayList<PhotoBean>) dataList);//传递数据
intent.putExtra("CURRENTPOSITION",position);
activity.overridePendingTransition(R.anim.scale_in, R.anim.scale_out);
activity.startActivity(intent);
});
}
/** 跟 BaseAdapter 的 getCount 作用一样。返回 */
@Override
public int getItemCount() {
return dataList == null ? 0 : dataList.size();
}
/** ViewHolder类,这个类用来初始化控件 */
static class ViewHolder extends RecyclerView.ViewHolder {
private ImageView item_photo;
ViewHolder(@NonNull View itemView) {
super(itemView);
item_photo=itemView.findViewById(R.id.iv_photo_item);
}
}
}
LayoutManager 布局管理器
LayoutManager
用于指定RecyclerView的布局方式。系统也为我们提供了三个实现类,同时提供了一个可供用户自定义布局管理抽象类RecycleView.LayoutManager
类
LinearLayoutManager
:线性布局, 支持纵向、横向(可以很轻松的实现横向listview的效果)
mRecyclerView.layoutManager = LinearLayoutManager(context上下文,显示方向,是否反转显示)
GridLayoutManager
:网格布局,线性布局是单行显示,网格布局可以设置并行显示的列数(竖直方向显示)
mRecyclerView.layoutManager = GridLayoutManager(上下文,显示行(列)数,显示方向,是否逆转显示)
StaggeredGridLayoutManage
:瀑布布局,类似淘宝商品展示的效果,可以交错显示
mRecyclerView.layoutManager = StaggeredGridLayoutManager(显示行(列)数,显示方向)
ItemDecoration 分割线
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
recycleView
提供了一个分割线类。 VERTICAL
表示分割线在item的底部,如果改为HORIZONTAL
则分割线在item的右侧。这个Decoration
类还可以设置 setDrawable()
要实现更加酷炫的分割线效果需要继承ItemDecoratio
类 进行自定义。
ItemDecoratio
类 中包含以下三个方法:
onDraw()
方法在drawChildren之前调用onDrawOver()
方法在drawChildren之后调用getItemOffsets()
方法中可以利用outRect为每个item设置一定的偏移量以实现给RecycleView的item添加边距
onDraw
和onDrawOver
方法针对的是RecycleView
本身。所以,在这两个方法中要遍历屏幕上可见的item,分别计算和绘制分割线。绘制完成后onDraw在item下面,item在中间,onDrawOver是可以盖住onDraw和item的
@Override
public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDraw(c, parent, state);
// 这个方法得到的 RecycleView 中 child view 的个数,也就是屏幕中显示的item的个数。并不是adapter中数据源的大小
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i ++) {
View child = parent.getChildAt(i);
int left = child.getLeft() + mDividerW / 2;
int right = child.getRight() + 60;
int top = child.getBottom() - mDividerW;
int bottom = child.getBottom() + mDividerW / 2;
c.drawRect(left, top, right, bottom, paint);
c.drawText("onDraw", (right)/2, bottom - 40, paintText);
}
}
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.onDrawOver(c, parent, state);
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i ++) {
View child = parent.getChildAt(i);
int left = child.getLeft() + mDividerW / 2;
int right = child.getRight() + 60;
int top = child.getTop() - mDividerW / 2;
int bottom = child.getTop() + mDividerW / 2;
c.drawRect(left, top, right, bottom, paintOver);
c.drawText("onDrawOver", (right)/2, bottom - 40, paintText);
}
}
getItemOffsets
方法针对的是每一个item
//这里我们设置item(left,top,right,bottom)四周的间隔分别为(20, 20, 0, 20)
outRect.set(20, 20, 0, 20);
当我们设置了recyclerView.addItemDecoration(官方分割线类/自定义类)
后,当recycleView
绘制的时候,会绘制我们设置的 Decoration。
ItemAnimator 动画
为RecycleView
设置动画,只要调用recyclerView.setItemAnimator(new DefaultItemAnimator());
方法即可,这也是一个抽象类,我们可以继承这个类自定义动画,同时系统也为我们提供了一个默认动画DefaultItemAnimator。
这里要注意呈现ItemAnimator
需要调用RecycleView.Adapter
中notifyItemRemoved(position)/notifyItemInserted();/notifyItemChanged
等方法,调用notifyDataSetChanged()
方法动画是无效的。
注意事项:实际增删操作过程中position不会自动增加,导致数据错位的问题,所以,当我们需要使用这些特效方法的时候,必须要重新刷新一遍数据,纠正position。或者用viewHolder.getAdapterPosition()获取我们需要的position进行数据操作
-
notifyDataSetChanged()
;列表全局刷新,给recyleview添加增删条目的动画时,调用这种刷新方法时无法生效的 -
notifyItemInserted(int position)
列表position位置添加一条数据时可以调用,伴有动画效果 -
notifyItemRemoved(int position)
列表position位置移除一条数据时调用,伴有动画效果 -
notifyItemMoved(int fromPosition, int toPosition)
列表fromPosition位置的数据移到toPosition位置时调用,伴有动画效果 -
notifyItemRangeChanged(int positionStart, int itemCount)
列表从positionStart位置到itemCount数量的列表项进行数据刷新 -
notifyItemRangeInserted(int positionStart, int itemCount)
列表从positionStart位置到itemCount数量的列表项批量添加数据时调用,伴有动画效果 -
notifyItemRangeRemoved(int positionStart, int itemCount)
列表从positionStart位置到itemCount数量的列表项批量删除数据时调用,伴有动画效果
RecycleView的优化策略
- 不要在onBindViewHolder中设置监听器,在onCreateViewHolder中设置监听器
- 使用DiffUtilRecyclerView.setHasFixedSize()方法
- 使用 LinearLayoutManager.setInitialPrefetchitemCount()方法
- 多个RectclerView共用RecycledViewPool.