美文网首页Android开发Android开发经验谈
RecyclerView使用以及避坑指南

RecyclerView使用以及避坑指南

作者: YoungerHu | 来源:发表于2019-10-31 14:39 被阅读0次

    基本使用

    • 继承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);
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:RecyclerView使用以及避坑指南

        本文链接:https://www.haomeiwen.com/subject/ocktbctx.html