RecyclerView使用以及避坑指南

基本使用

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

    public class HelloRecyclerAdapter extends RecyclerView.Adapter {
      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 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 {
        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 payloads) {
            if (holder instanceof HeaderViewHolder) {
                bindHeaderView((HeaderViewHolder) holder, payloads);
            } else {
                bindNormalView((NormalViewHolder) holder, position, payloads);
            }
        }
    
        private void bindHeaderView(HeaderViewHolder holder, List 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 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);
            }
        }
    }
     
    
                                
                            
                        
                        
                        

    你可能感兴趣的:(RecyclerView使用以及避坑指南)