RecyclerView的重构之路(七)

目标

本篇是RecyclerView的重构之路系列的第七篇, 讲解IDouban项目中RecylcerView.ViewHolderRecylcerView.Adapter的重构。目的是 干掉功能以及代码类似的BookAdpaterMoviesAdapter。抱歉,到现在才切入正题,我承认是标题党

代码分析

在做重构之前,回顾下,所写的代码功能: 获取书籍并展示,获取电影并展示。

代码目录结构

RecyclerView的重构之路(七)_第1张图片
重构前后对比图

有同学会说,木丁老师,重构代码是越写越多啊? 文件也还增加了。注意,小智同学,重构的目的是解耦!是让废代码、重复代码消失,让扩展性好一些。不能以文件数量多少,代码多少为标准。IDouban除了RecyclerView之外,还有很多废代码。
如图所示,右边添加了新包common,存放通用类, 暂时添加了Adapter, ViewHolder2个类。

关键类继承图

RecyclerView的重构之路(七)_第2张图片
类继承图

本次重构需要干掉BookAdapter, MoviesAdapter, 细看上图, 红框代表重构之前的ViewHolder & xxxAdapter 类继承关系。篮框代表重构之后的ViewHolder & Adapter, 注意其中,BookAdapter, MoviesAdapter不见了,取代他们是类Adapter, 并且使用了泛型。

关键代码

从2个方面展开, 1. ViewHolder; 2. Adapter

ViewHolder

public abstract class ViewHolder extends RecyclerView.ViewHolder {
    protected T itemContent;

    public ViewHolder(@NonNull View itemView) {
        super(itemView);
    }

    public void updateItem(T itemContent) {
        this.itemContent = itemContent;
        onBindItem(itemContent);
    }

    protected abstract void onBindItem(T itemContent);

    public interface Builder {
        VH build(View itemView);
    }
}

ViewHolder类继承自RecyclerView.ViewHolder并且使用泛型, 其中T表示泛型,这里理解为BookMovies... ...

  • ViewHolder构造方法必须写
  • updateItem(T itemContent) {...}提供给Adapter使用
  • onBindItem是在ViewHolder具体子类中使用。因为,不同的ViewHolder绑定的布局内容是不一致的
  • interface Builder 获得传入Adapter中的是哪种ViewHolder实例

Adapter

public class Adapter> extends RecyclerView.Adapter {
    List data;
    ViewHolder.Builder builder;

    @LayoutRes
    int layoutResId;

    public Adapter(@NonNull List data, @LayoutRes int layoutResId, ViewHolder.Builder builder) {
        this.data = data;
        this.layoutResId = layoutResId;
        this.builder = builder;
    }

    @NonNull
    @Override
    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(layoutResId, parent, false);
        return builder.build(itemView);
    }

    @Override
    public void onBindViewHolder(VH holder, final int position) {
        if (holder == null) return;
        holder.updateItem(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    public void setData(List data) {
        this.data = data;
        notifyDataSetChanged();
    }

    public T getItem(int pos) {
        return data.get(pos);
    }
}
  1. public class Adapter> extends RecyclerView.Adapter
    Adapter的是一个桥, 链接 数据 和 ViewHolder, 这里的Adapter是泛型类,其中的T表示BookMovies等,VHViewHolder的子类,这里约束了VH必须是我们自定义的ViewHolder
    简单来说,AdapterRecyclerView.Adapter的子类,并且,Adapter是泛型类。

  2. List data; 存放外界传入的数据列表

  3. ViewHolder.Builder builder;用于onCreateViewHolder方法中获取特定的ViewHolder子类

  4. int layoutResId; 传入RecyclerViewitemview所需布局id

  5. public Adapter(@NonNull List data, @LayoutRes int layoutResId, ViewHolder.Builder builder)
    构造方法,传入关键性参数。

  6. onCreateViewHolder必须实现的方法, 难点在于, 无法直接return 出所需要的VH, 因为VH是泛型化,这里没法直接通过 return new VH(itemview)方式获得实例,需要在某个调用点的地方才知道传入的是哪种ViewHolder的子类。此处借鉴Builder模式,在使用到的时候,才建造对应的ViewHolder子类对象。

  7. onBindViewHolder 必须覆盖的方法,其中关键是 holder.updateItem()方法。

  8. getItemCount 必须覆盖的方法, 用于确定有多少数据。

  9. setData 外界更新数据列表

  10. getItem 让外界获取对应pos的数据, 暂未使用!

上述代码, 其中, 第6点不好理解, 其他不难。

如何使用

关键点是Adpater的初始化。

mBookAdapter = new Adapter<>(mBookList, R.layout.recyclerview_book_item, new ViewHolder.Builder() {
                @Override
                public BookViewHolder build(View itemView) {
                    return new BookViewHolder(itemView);
                }
            });
mMovieAdapter = new Adapter<>(mMoviesList, R.layout.recyclerview_movies_item, new ViewHolder.Builder() {
                @Override
                public MoviesViewHolder build(View itemView) {
                    return new MoviesViewHolder(itemView);
                }
            });

github代码

本篇代码已经上传github, 并且添加的tag release_02版本,查看第一次重构后的代码直接使用如下方法:

  • **git clone https://github.com/tancolo/IDouban.git **
  • ** git checkout release_02 就可以取得 tag 对应的代码了。**

但是这时候 git 可能会提示你当前处于一个“detached HEAD" 状态,因为 tag 相当于是一个快照,是不能更改它的代码的,如果要在 tag 代码的基础上做修改,你需要一个分支:

git checkout -b your_branch_name release_02

这样会从 tag 创建一个分支,然后就和普通的 git 操作一样了。

您要是觉得好,请点个赞,加个星!

RecyclerView的重构之路(八)

你可能感兴趣的:(RecyclerView的重构之路(七))