美文网首页
Android如何复用同一个RecyclerViewAdapte

Android如何复用同一个RecyclerViewAdapte

作者: 杨静小胖胖 | 来源:发表于2018-10-30 15:52 被阅读0次

    前言

    对于一个Android应用来说,列表是经常需要使用的一个控件,而实现列表对于Android系统来说一般就是使用ListView和RecycleView这两个,由于RecycleView更为灵活高效,所以大部分Android开发者更倾向于RecycleView。大家都知道如果要使用RecycleView必须要有一个与之相伴的Adapter。但是大部分简单的列表的Adapter功能都一致,那可不可以只使用一个BaseAdapter来控制RecyclerView呢,这样可能减少很多重复性的代码,我最近正好写了一个简单的项目。整个项目只使用了一个BaseAdapter。下面我来给读者分享一下我的尝试。

    内容

    其实这个东西很简单,就是一个抽象的过程。把大部分列表都需要的功能都抽象出来做成一个通用的类,然后剩下的数据加载。View控制这些多态性的东西留给使用者自己去实现就可以了,总之就是用最少的代码实现相同的功能。

    那么那些东西是通用的呢,来举例子吧

    • 设置一个List数据集合
    • 重写getItemCount()
    • 重写onCreateViewHolder(单布局)
    • 追加数据到集合末尾,适用于分页加载
    • 设置数据源并更新界面,适用于下啦刷新
    • 删除某个子项数据并更新界面
    • 通过某个下标获取数据
    • 设置item的点击事件监听
    • 通过反射加载ViewHolder

    上述的都是可以抽象出来的。这个时候我们就可以自己先实现一个BaseAdapter去实现这些基础的功能,然后有那么东西是不可以抽象出来的呢?
    同样举例

    • onBindViewHolder()

    这个接口是留给Adapter用户将数据加载到对应的View上的,每个列表的控件和数据都不一样,自然是不可以复用的。所以这个要作为一个接口留出来给使用者重写。那么明白了就来看看代码吧

    public class BaseRecycleAdapter<T, V extends BaseViewHolder<T>> extends RecyclerView.Adapter<V> {
    
      protected List<T> dataList;
    
      private int layoutId;
    
      private OnItemClickListener<T> onItemClickListener;
    
      private OnItemLongClickListener<T> onItemLongClickListener;
    
      private Context mContext;
      private Class<V> vClass;
    
      public BaseRecycleAdapter(List<T> dataList, int layoutId, Class<V> vClass) {
        this.dataList = dataList;
        this.layoutId = layoutId;
        this.vClass = vClass;
      }
    
      public BaseRecycleAdapter(int layoutId, Class<V> vClass) {
        this(null, layoutId, vClass);
      }
    
      @NonNull
      @Override
      public V onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        if (mContext == null) mContext = viewGroup.getContext();
        try {
          Constructor<V> constructor = vClass.getConstructor(View.class);
          return constructor.newInstance(newItemView(viewGroup, layoutId));
        } catch (NoSuchMethodException | IllegalAccessException
            | InstantiationException | InvocationTargetException e) {
          e.printStackTrace();
          LogHelper.log("反射失败");
        }
        return null;
      }
    
      private View newItemView(ViewGroup viewGroup, int resId) {
        return LayoutInflater.from(viewGroup.getContext()).inflate(resId, viewGroup, false);
      }
    
      @Override
      public void onBindViewHolder(@NonNull V baseViewHolder, int i) {
        baseViewHolder.setData(dataList.get(i));
        baseViewHolder.loadItemData(mContext, dataList.get(i), i);
        if (onItemClickListener != null) {
          baseViewHolder.setOnClickListener(v
              -> onItemClickListener.onItemClick(dataList.get(i), v, i));
        }
        if (onItemLongClickListener != null) {
          baseViewHolder.itemView.setOnLongClickListener(v ->
              onItemLongClickListener.onItemLongClick(dataList.get(i), i));
        }
      }
    
      @Override
      public int getItemCount() {
        return dataList == null ? 0 : dataList.size();
      }
    
      public void appendDataToList(T data) {
        if (dataList == null) {
          dataList = new ArrayList<>();
        }
        dataList.add(data);
        notifyItemInserted(getItemCount() - 1);
      }
    
      /**
       * 用于上拉加载更多更新界面
       */
      public void appendDataToList(List<T> datas) {
        int firstPosition = getItemCount();
        dataList.addAll(datas);
        int lastPosition = getItemCount();
        for (int i = firstPosition; i < lastPosition; i++) {
          notifyItemInserted(i);
        }
      }
    
      public T getItemData(int position) {
        if (0 <= position && position < getItemCount()) {
          return dataList.get(position);
        }
        return null;
      }
    
      /**
       * 用于下啦
       */
      public void setDataList(List<T> dataList) {
        this.dataList = dataList;
        notifyDataSetChanged();
      }
    
      public void removeItemFormList(int position) {
        if (position < getItemCount()) {
          dataList.remove(position);
          notifyItemRemoved(position);
          for (int i = position; i < dataList.size(); i++) {
            notifyItemChanged(i);
          }
        }
      }
    
      public List<T> getData() {
        return dataList == null ? new ArrayList<>(0) : dataList;
      }
    
      public void refreshItemData(T data, int position) {
        dataList.set(position, data);
        notifyItemChanged(position);
      }
    
      public void refreshRangeData(int start, List<T> datas) {
        int end = start + datas.size();
        if (start < 0 || end > getItemCount()) {
          return;
        }
        for (int i = start; i < end; i++) {
          dataList.set(i, datas.get(i - start));
        }
        notifyItemRangeChanged(start, datas.size());
      }
    
      public void setOnItemClickListener(OnItemClickListener<T> onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
      }
    
      public void setOnItemLongClickListener(
          OnItemLongClickListener<T> onItemLongClickListener) {
        this.onItemLongClickListener = onItemLongClickListener;
      }
    }
    
    

    下面是点击和长按事件监听代码:

    //点击事件监听
    public interface OnItemClickListener<D> {
    
      void onItemClick(D itemData, View view,int position);
    
    }
    
    //长按事件监听
    public interface OnItemLongClickListener<D> {
    
      boolean onItemLongClick(D data, int position);
    
    }
    

    这是我实现的一个通用的单布局Adapter,从这里看,是不是只有一些必须或者通用方法的实现。因为onBindViewHolder这个方法里面item的布局更新是需要留给使用者重写的,所以把这个留给继承BaseViewHolder并重写loadItemData的子ViewHolder来实现
    看一下BaseViewHolder的代码

    public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder {
    
      public int currentPosition;
    
      private T data;
    
      public BaseViewHolder(@NonNull View itemView) {
        super(itemView);
        initItemView(itemView);
      }
    
      protected abstract void initItemView(View view);
    
      public abstract void loadItemData(Context context, T data, int position);
    
      public void onViewRecycled() {
    
      }
    
      public T getData() {
        return data;
      }
    
      public void setData(T data) {
        this.data = data;
      }
    
      public void setOnClickListener(View.OnClickListener onClickListener) {
        itemView.setOnClickListener(onClickListener);
      }
    }
    
    

    使用者只需要继承BaseViewHolder并重写抽象方法,在initItemView中findView在loadItemData中重写布局更新,然后在Activity直接服用BaseAdapter即可

    如下所示
    先实现一个ViewHolder

    public class CourseDataViewHolder extends BaseViewHolder<Boolean> {
    
      @BindView(R.id.tv_number)
      TextView tvNumber;
      @BindView(R.id.cv_date)
      CardView cvDate;
    
      public CourseDataViewHolder(@NonNull View itemView) {
        super(itemView);
      }
    
      @Override
      protected void initItemView(View view) {
        ButterKnife.bind(this, view);
      }
    
      @Override
      public void loadItemData(Context context, Boolean data, int position) {
        if (data) {
          cvDate.setCardBackgroundColor(UiHelper.getColor(R.color.colorPrimary));
        } else {
          cvDate.setCardBackgroundColor(UiHelper.getColor(R.color.bg_no));
        }
        tvNumber.setText(String.valueOf(position + 1));
      }
    }
    

    然后这样复用即可

    new BaseRecycleAdapter<>(data, R.layout.item_course_date, CourseDataViewHolder.class);
    

    同理当需要使用Adapter的通用方法时,直接调用即可。比如给item添加点击监听

    代码如下所示

      baseRecycleAdapter.setOnItemClickListener(new OnItemClickListener() {
          @Override
          public void onItemClick(Object itemData, View view, int position) {
            //重写item被点击后要处理的事情
          }
        });
    

    总结

    其实这只是一个简单的抽象过程。使用多了,也就明白了

    相关文章

      网友评论

          本文标题:Android如何复用同一个RecyclerViewAdapte

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