RecyclerView使用以及避坑指南

基本使用
  • 继承RecyclerView.Adapter,并在内部自定义对应的ViewHolder

    public class HelloRecyclerAdapter extends RecyclerView.Adapter<HelloRecyclerAdapter.ViewHolder> {
      private List dataList = new ArrayList();
    
      public HelloRecyclerAdapter(List dataList) {
          this.dataList = dataList;
      }
    
      @NonNull
      @Override
      public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
          View view = LayoutInflater.from(parent.getContext())
                  .inflate(R.layout.item_hello_recycler, parent, false);
          return new ViewHolder(view);
      }
    
      @Override
      public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
          Object data = dataList.get(position);
          //把data的数据赋值到holder对应的View中
      }
    
      @Override
      public int getItemCount() {
          return dataList.size();
      }
    
      class ViewHolder extends RecyclerView.ViewHolder {
          public ViewHolder(View itemView) {
              super(itemView);
          }
      }
    }
    
  • Activity中新建adapter并赋值给recycler

    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    //list是已赋值数据
    recyclerView.setAdapter(new HelloRecyclerAdapter(list));
    

    以上,一个recyclerView的列表就出来了。

RecyclerView中添加header
  • 先定义两个常量,用来区分header和普通情况

    private static final int HEADER = 1;
    private static final int NORMAL = 2;
    
  • 建立另一个HeaderViewHolder

    class HeaderViewHolder extends RecyclerView.ViewHolder {
      public HeaderViewHolder(View itemView) {
        super(itemView);
      }
    }
    
  • 然后onCreateViewHolder中分情况创建

    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
      View view;
      if (viewType == HEADER) {
        view = LayoutInflater.from(parent.getContext())
          .inflate(R.layout.item_hello_recycler, parent, false);
      } else {
        view = LayoutInflater.from(parent.getContext())
          .inflate(R.layout.item_hello_recycler_head, parent, false)
      }
    
      return new ViewHolder(view);
    }
    
  • getItemCount中增加对应数量

    @Override
    public int getItemCount() {
    //增加header节点
    return dataList.size() + 1;
    }
    
  • onBindViewHolder中分情况绑定数据

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
      Object data = dataList.get(position);
      if (holder instanceof HeaderViewHolder) {
        //绑定header数据
      } else {
        //绑定普通数据
      }
    }
    

当然这只是最基本的能用了,在使用过程中会遇到一些奇奇怪怪的问题,接下来我们一一解决


recyclerView中数据错乱问题
  • 数据来源固定

    这种情况很好解决,主要就是每个item在赋值时候,要把各个情况都覆盖到,不能只覆盖一种情况。

    假设item内有收藏功能,收藏后会用UI的变化,我们可以用如下方法实现:

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
      Object data = dataList.get(position);
      //这里一定不能只写if而不写else,否则会出现颜色错乱
      if(data.isCollection()){
        holder.collectionView.setTextColor(RED)
      }else{
        holder.collectionView.setTextColor(BLACK)
      }
    }
    
  • 数据来自网络

    这类情况的常见情景是加载图片,当网络环境较差时,图片加载完后,你可能已经翻页了,这时候再去显示图片会出现错乱的情况。

    针对这一情况,我们可以给图片添加一个TAG,显示图片时候判断对应的TAG是否一致

    holder.ivCameraImages.setBackground(R.drawable.place_holder);
    
    holder.ivCameraImages.setTag(imageURL);
    
    @Override
    public void handleMessage(Message msg) {
      super.handleMessage(msg);
      if (msg.what == MSG_IMAGE) {
        Bitmap bm = (Bitmap) msg.obj;
        if (bm != null) {
          if (TextUtils.equals((String) imageView.getTag(), imageURL)){
            imageView.setBackground(new BitmapDrawable(bm));
          }
        }
      }
    }
    

    当然,目前的主流图片框架会把url作为对应的TAG添加到ImageView中,省了我们重复造轮子的时间。

RecyclerView中的局部刷新

还是回到那个收藏的问题,如果点击收藏按钮,只更新当前的item这个需求如何实现?

  • 当列表中没有图片时候

    public void collectionSuccess(String id) {
      //当header的收藏数量变化时候
      //通过ID找到下标
      int position = findPositionForId(id);
      //更新数量
      dataList.get(position)
        .setCollectionNum(data.getCollectionNum()+1);
      //更新对应UI
      notifyItemChanged(position);
    }
    
  • 列表中存在图片时

    当有图片时候,使用notifyItemChanged()方法会导致图片闪烁。

    解决这个问题呢就需要进行一些大的改动了。

    首先,放弃使用两个参数的onBindViewHolder方法,使用三个参数的方法:

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
      //不去覆写这个方法
    }
    
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
      if (payLoads != null && payLoads.size() > 0) {
        //有部分数据更新的情况
        String s = (String) payLoads.get(0);
        switch (s) {
          case STAR:
            //点赞数量UI更新
            updateStar(holder, dataList.get(position - 1));
            break;
          case COLLECTION:
            //收藏数量UI更新
            updateCollection(holder, dataList.get(position - 1));
            break;
        }
      } else {
        //正常情况
      }
    }
    

    然后,数据发生改变时,调用的方法也有变化

    public void collectionSuccess(String id) {
      //当header的收藏数量变化时候
      //通过ID找到下标
      int position = findPositionForId(id);
      //更新数量
      dataList.get(position).setCollectionNum(data.getCollectionNum()+1);
      //更新对应UI
      notifyItemChanged(position,COLLECTION);
    }
    

    这样设置完后,局部更新数据就不会导致item有变化了。


最后,我把整个带Header的,可以局部更新UI的adapter基本框架都放出来,大家有需要的自行取用

完整结构

public class HelloRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int HEADER = 1;
    private static final int NORMAL = 2;
    private static final String COLLECTION = "collection";
    private static final String STAR = "star";

    private Context context;
    private Object headData;
    private List dataList;

    public HelloRecyclerAdapter(Context mContext,
                                Object headerData,
                                List answerList) {
        this.context = mContext;
        this.headData = headerData;
        this.dataList = answerList;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == HEADER) {
            View view = LayoutInflater.from(context)
                    .inflate(R.layout.item_header_view, parent, false);
            return new HeaderViewHolder(view);
        } else {
            View view = LayoutInflater.from(context)
                    .inflate(R.layout.item_normal_view, parent, false);
            return new NormalViewHolder(view);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        //不去覆写这个方法
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
        if (holder instanceof HeaderViewHolder) {
            bindHeaderView((HeaderViewHolder) holder, payloads);
        } else {
            bindNormalView((NormalViewHolder) holder, position, payloads);
        }
    }

    private void bindHeaderView(HeaderViewHolder holder, List<Object> payLoads) {
        if (payLoads != null && payLoads.size() > 0) {
            String payLoad = (String) payLoads.get(0);
            switch (payLoad) {
                case COLLECTION:
                    //header收藏数量更新
                    updateCollection(holder);
                    break;
                case STAR:
                    //header点赞数量更新
                    updateStar(holder);
                    break;
            }
        } else {
            //正常绑定数据
        }
    }

    private void bindNormalView(NormalViewHolder holder, int position, List<Object> payLoads) {
        if (payLoads != null && payLoads.size() > 0) {
            String s = (String) payLoads.get(0);
            switch (s) {
                case STAR:
                    //点赞数量更新
                    updateStar(holder, dataList.get(position - 1));
                    break;
                case COLLECTION:
                    //收藏数量更新
                    updateCollection(holder, dataList.get(position - 1));
                    break;
            }
        } else {
            //绑定正常数据
        }
    }

    @Override
    public int getItemCount() {
        return dataList.size() + 1;
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0) {
            return HEADER;
        } else {
            return NORMAL;
        }
    }

    public void collectionSuccess(String id) {
        //当header的收藏数量变化时候
        //通过ID找到下标
        int position = findPositionForId(id);
        //更新数量
        dataList.get(position).setCollectionNum(data.getCollectionNum()+1);
        //更新对应UI
        notifyItemChanged(position,COLLECTION);
    }

    public void disCollectionSuccess(String id) {
        //当header的收藏数量变化时候
        notifyItemChanged(0, COLLECTION);
    }

    public void starSuccess(String id) {
        int position = 0;
        //略去通过ID寻找position过程
        //略去通过ID更改对应数据的点赞数量
        notifyItemChanged(position, STAR);
    }

    public void disStarSuccess(String id) {
        int position = 0;
        //略去通过ID寻找position过程
        //略去通过ID更改对应数据的点赞数量
        notifyItemChanged(position, STAR);
    }

    class HeaderViewHolder extends RecyclerView.ViewHolder {
        public HeaderViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }

    class NormalViewHolder extends RecyclerView.ViewHolder {
        public NormalViewHolder(View itemView) {
            super(itemView);
            ButterKnife.bind(this, itemView);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值