Android如何复用同一个RecyclerViewAdapter

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34379015/article/details/83543685

前言

对于一个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;
  }
}

这是我实现的一个通用的单布局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被点击后要处理的事情
      }
    });

总结

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

猜你喜欢

转载自blog.csdn.net/qq_34379015/article/details/83543685