RecycleView的一些使用

Source

依赖

在项目的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 对应代码
基本的步骤为:

  1. RecyclerView新增适配器PhotoAdapter ,并让其继承于RecyclerView.Adapter,把泛型指定为PhotoAdapter .ViewHolder
    定义内部类ViewHolder,并继承RecyclerView.ViewHolder。传入的View参数通常是RecyclerView子项的最外层布局。

  2. PhotoAdapter 构造函数,用于把要展示的数据源传入,并赋予值给全局变量dataList

  3. 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添加边距

onDrawonDrawOver方法针对的是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.AdapternotifyItemRemoved(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.