BRVAH:基本使用源码分析

简单介绍

BRVAH:是由大佬 陈宇明 大佬开源的一款 RecyclerView 的Adapter 框架。官方介绍在此 http://www.recyclerview.org/ 。首先表示非常感谢与佩服!

废话开篇

本人由于一直想要深入 RecyclerView 的学习与源码的阅读(在那么多人的文章带领下肚了很多源码都记不住,无奈面试的时候一直被问读了什么源码,)。而在自己与公司的项目中也一直使用这个框架,所以就想要深入学习一下这个框架。

这将是一系列文章,仅仅记录我阅读这个源码时的思路与理解。关于有意义和没意义这件事,嗯~~~~~~~我开心就好。所以如果有人不小心看到我的这个文章,希望不会辣你的眼睛。如果你读完了,我表示感谢。如果你发现了错误,欢迎指正

本文思路

  1. 分析RecyclerView.Adapter 的基本使用来找出需要关注的重点
  2. 通过 分析 BRVAH 的基本使用 找出 BRVAH 的重点
  3. 按照重点的先后顺序逐个分析
  4. 总结

RecyclerView.Adapter 的基本使用

  1. xml中声明RecyclerView

  1. Activity 通过setAdapterRecyclerView 赋值 Adapter
private RecyclerView mRecyclerView;
private DemoAdapter mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_demo);
    mRecyclerView = findViewById(R.id.recycler_view);
    mAdapter = new DemoAdapter(getData());
    mRecyclerView.setAdapter(mAdapter);
}
// 生成
private List getData() {
    List data = new ArrayList<>();
    for (int i = 0; i < 30; i++) {
        data.add("item" + i);
    }
    return data;
}
  1. 接下来看 DemoAdapter。自定义 Adapter 需要继承自 RecyclerView.Adapter,并制定 ViewHolder 的范型
public class DemoAdapter extends RecyclerView.Adapter {
        // 很简单的数据项
    private List mData;

    public DemoAdapter(List data) {
        mData = data;
    }
        // 重点1:创建泛型制定的 ViewHolder
    @NonNull
    @Override
    public DemoViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.demo_adapter, viewGroup, false);
        return new DemoViewHolder(itemView);
    }
        // 重点2:在这里借助 ViewHodler 操作每一个 position 中的 View
    @Override
    public void onBindViewHolder(@NonNull DemoViewHolder demoViewHolder, int position) {
        demoViewHolder.mTvItem.setText(mData.get(i));
    }
        // 重点3:返回 item 个数
    @Override
    public int getItemCount() {
        return mData == null ? 0 : mData.size();
    }
        // 重点4:ViewHolder 提供可供操作的 ItemView极其子View
    class DemoViewHolder extends RecyclerView.ViewHolder {
        private TextView mTvItem;

        public DemoViewHolder(@NonNull View itemView) {
            super(itemView);
            mTvItem = itemView.findViewById(R.id.tv_item);
        }
    }
}

以上重点1、重点2、重点3对应于 BaseQuickAdapter,重点4对应于 BaseViewHolder 将在接下来被着重分析。首先我们看一下 BRVAH 的基本使用。

BRVAH 的基本使用

  1. 创建 BRVAHDemoAdapter 并继承自 BaseQuickAdapter,并指定数据类型为 String , ViewHolder 类型为 BaseViewHolder
// 重点5 指定item数据类型与 BaseViewHolder
public class BRVAHDemoAdapter extends BaseQuickAdapter {
    public BRVAHDemoAdapter(@Nullable List data) {
            // 重点6 调用父类构造方法
        super(R.layout.demo_adapter, data);
    }
        // 借助 BaseViewHolder 实现数据与UI的绑定
    @Override
    protected void convert(BaseViewHolder helper, String item) {
        helper.setText(R.id.tv_item, item);
    }
}
  1. 创建 BRVAHDemoAdapter 对象 并替换原来的 DemoAdpter 对象赋值给 RecyclerView。可以看到实现了与原来一样的效果,但是代码量却大大地减少,真的是灰常优雅了。

通过 RecyclerView.Adapter 的基本使用和 BRVAH 的基本使用对比,接下来将会按照 重点5—>重点6 —> 重点1 —>重点2—> 重点3 —>重点4 的顺序进行分析。

BRVAH 的实现分析

重点5:BaseQuickAdapter的声明

public abstract class BaseQuickAdapter extends RecyclerView.Adapter{//...}
public class BaseViewHolder extends RecyclerView.ViewHolder{//...}

可以看到 BaseViewHolder 继承自RecyclerView.ViewHolder

  1. BaseQuickAdapter的声明如上所示:可以看到 BaseQuickAdapter 是一个抽象类,继承自RecyclerView.AdapterT 用于表示数据项的类型;K 表示继承自 BaseViewHolder 的类型。
  2. 再看BaseViewHolder,继承自RecyclerView.ViewHolder。嗯?这不是和我们原来直接继承RecyclerView.Adapter 一样了吗?

重点6:BaseQuickAdapter 构造方法

三个重载构造方法,最终都调用BaseQuickAdapter(@LayoutRes int layoutResId, @Nullable List data)

// layoutResId:布局id;data:数据项,为空就创建一个空的 ArrayList
public BaseQuickAdapter(@LayoutRes int layoutResId, @Nullable List data) {
    // mData 表示内部维护的数据项声明,没有则直接新建一个空的 ArrayList
    this.mData = data == null ? new ArrayList() : data;
    if (layoutResId != 0) {
        this.mLayoutResId = layoutResId;
    }
}

public BaseQuickAdapter(@Nullable List data) {
    this(0, data);
}

public BaseQuickAdapter(@LayoutRes int layoutResId) {
    this(layoutResId, null);
}

重点1:onCreateViewHolder

该方法返回一个 BaseViewHolder或其子类,具体看以下注释

@Override
public K onCreateViewHolder(ViewGroup parent, int viewType) {
    K baseViewHolder = null;
    this.mContext = parent.getContext();
    // 获取 LayoutInflater,后续 getItemView 中会用到
    this.mLayoutInflater = LayoutInflater.from(mContext);
    // 根据不同的 viewType 创建不同的 ViewHolder
    switch (viewType) {
        // ...先忽略这一部分
        default:
                // 分析1:创建默认的 ViewHolder
            baseViewHolder = onCreateDefViewHolder(parent, viewType);
                // 绑定监听事件 先忽略
            bindViewClickListener(baseViewHolder);
    }
    // 传入当前Adapter到BaseViewHolder中
    baseViewHolder.setAdapter(this);
    return baseViewHolder;
}
// 分析1: onCreateDefViewHolder 
protected K onCreateDefViewHolder(ViewGroup parent, int viewType) {
    int layoutId = mLayoutResId;
    // 先忽略
    if (mMultiTypeDelegate != null) {
        layoutId = mMultiTypeDelegate.getLayoutId(viewType);
    }
    // 分析2:调用 createBaseViewHolder
    return createBaseViewHolder(parent, layoutId);
}
// 分析2
protected K createBaseViewHolder(ViewGroup parent, int layoutResId) {
    // 分析3:getItemView(@LayoutRes int layoutResId, ViewGroup parent)
    // 分析4:createBaseViewHolder(View view)
    return createBaseViewHolder(getItemView(layoutResId, parent));
}
// 分析3 是不是很熟悉,借助 LayoutInflater 根据 layoutResId 返回一个 View
// 然后将这个View 传递给 ViewHolder。这和我们在 DemoAdapter#onCreateViewHolder中做的不是一样要到嘛
protected View getItemView(@LayoutRes int layoutResId, ViewGroup parent) {
    return mLayoutInflater.inflate(layoutResId, parent, false);
}
// 分析4:根据泛型和反射获取 K 的实际类型
protected K createBaseViewHolder(View view) {
    Class temp = getClass();
    Class z = null;
    while (z == null && null != temp) {
        // 获取 K 的实际类型
        z = getInstancedGenericKClass(temp);
        temp = temp.getSuperclass();
    }
    K k;
    // 泛型擦除会导致z为null
    if (z == null) {
        k = (K) new BaseViewHolder(view);
    } else {
        // 使用反射 根据 K 的实际类型创建对象
        k = createGenericKInstance(z, view);
    }
    return k != null ? k : (K) new BaseViewHolder(view);
}
// 获取泛型类型
private Class getInstancedGenericKClass(Class z) {
    // 获取当前父类的范型类型
    Type type = z.getGenericSuperclass();
    // 父类是否包含泛型
    if (type instanceof ParameterizedType) {
        // 获取泛型数组
        Type[] types = ((ParameterizedType) type).getActualTypeArguments();
        // 遍历泛型数组
        for (Type temp : types) {
            // 如果这个泛型是一个类
            if (temp instanceof Class) {
                Class tempClass = (Class) temp;
                // isAssignableFrom 表示 tempClass 是否是 BaseViewHolder 的子类或者接口
                if (BaseViewHolder.class.isAssignableFrom(tempClass)) {
                    return tempClass;
                }
            } else if (temp instanceof ParameterizedType) {
                //  如果这个泛型是一个泛型参数,那么就获取他的实际类型
                Type rawType = ((ParameterizedType) temp).getRawType();
                if (rawType instanceof Class && BaseViewHolder.class.isAssignableFrom((Class) rawType)) {
                    return (Class) rawType;
                }
            }
        }
    }
    return null;
}

private K createGenericKInstance(Class z, View view) {
    try {
        Constructor constructor;
        // 如果是内部类,且不是静态类
        if (z.isMemberClass() && !Modifier.isStatic(z.getModifiers())) {
            // 获取指定的、不包括继承的构造函数
            constructor = z.getDeclaredConstructor(getClass(), View.class);
            // 屏蔽Java 语言的访问检查,使得构造函数可以访问
            constructor.setAccessible(true);
            // 为什么要传入 this,不是特别明白
            return (K) constructor.newInstance(this, view);
        } else {
            // 同上,不再重复说明
            constructor = z.getDeclaredConstructor(View.class);
            constructor.setAccessible(true);
            return (K) constructor.newInstance(view);
        }
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return null;
}

从以上注释可以看到:BaseQuickAdapter 内部重写了 onCreateViewHolder 然后通过反射或者 new 的方式返回一个 BaseViewHolder

重点2:onBindViewHolder

二话不说,直接先上代码:

@Override
public void onBindViewHolder(K holder, int position) {
    autoUpFetch(position);
    autoLoadMore(position);
    int viewType = holder.getItemViewType();

    switch (viewType) {
        case 0:
            convert(holder, getItem(position - getHeaderLayoutCount()));
            break;
        case LOADING_VIEW:
            mLoadMoreView.convert(holder);
            break;
        case HEADER_VIEW:
            break;
        case EMPTY_VIEW:
            break;
        case FOOTER_VIEW:
            break;
        default:
                // 以上可以先 忽略
                // 分析1: getHeaderLayoutCount() 获取是否存在 Header 
                // 分析2: getItem(position - getHeaderLayoutCount()) 用于获取当前
                // position对应的 data 数据,当前 position 需要去除头部个数(1或0)
                // 分析3:
            convert(holder, getItem(position - getHeaderLayoutCount()));
            break;
    }
}
// 分析1: 如果存在 Header 就返回1 否则返回0
public int getHeaderLayoutCount() {
    if (mHeaderLayout == null || mHeaderLayout.getChildCount() == 0) {
        return 0;
    }
    return 1;
}

// 分析2:从 mData 获取 position 对应的 data
public T getItem(@IntRange(from = 0) int position) {
    // 对 position 做安全范围判断
    if (position >= 0 && position < mData.size())
        return mData.get(position);
    else
        return null;
}
// 分析3: 子类通过重写父类的这个方法,拿到 BaseViewHolder 和Item数据
// 并在这个方法中对 Item 项实现数据操作
protected abstract void convert(K helper, T item);

从以上代码可以看出:BaseQuickAdapter 通过重写 onBindViewHolder 方法,对于一般情况,通过 convert 抽象方法向子类暴露了 BaseViewHolder 和 当前位置的 数据项 以供开发者使用。

重点3:getItemCount

区别于我们自定义的 Adapter,在计算 item 个数的时候,需要考虑 EmptyViewHeaderFooter 的情况

@Override
public int getItemCount() {
    int count;
    // 是否存在 EmptyView。后续分析
    if (1 == getEmptyViewCount()) {
        // 存在 EmptyView; 则count = 1 + header(1/0) + footer(1/0)
        count = 1;
        if (mHeadAndEmptyEnable && getHeaderLayoutCount() != 0) {
            count++;
        }
        if (mFootAndEmptyEnable && getFooterLayoutCount() != 0) {
            count++;
        }
    } else {
        // 数量等于 (1/0) + 数据项 + (1/0) + (1/0)
        count = getHeaderLayoutCount() + mData.size() + getFooterLayoutCount() + getLoadMoreViewCount();
    }
    return count;
}

在重点2中我们看到,最终生成了一个 BaseViewHolder 对象并在重点 3 中通过 convert 方法向子类暴露用来操作 Item 的UI。接下来我们就看看 BaseViewHolder 内部都做了什么。

重点4:BaseViewHolder

BaseViewHolder 最大的特点就是通过一个泛型方法 getView和一系列 setXXX 方法。使得代码的调用更加简洁。

setXXX()

以上方法大都类似,选一个我们最常用的设置文字方法看一下:

public BaseViewHolder setText(@IdRes int viewId, CharSequence value) {
    // 分析1 getView 获取 TextView 实例
    // 并设置文字
    TextView view = getView(viewId);
    view.setText(value);
    return this;
}
//分析1
public  T getView(@IdRes int viewId) {
    // views 是一个 SparseArray 对象 
    // 以下代码达到仅在第一此需要某个 View 时通过 findViewById 获取
    // 以后都从 views 通过 id 获取
    // 这是 Flyweight 设计模式的思想嘛
    View view = views.get(viewId);
    if (view == null) {
        view = itemView.findViewById(viewId);
        views.put(viewId, view);
    }
    return (T) view;
}

总结

基本用法的分析就结束啦,接下来是总结。通过以上的分析,我有以下的收获:

  1. 明白了 BRVAH 基本实现原理,其本质和我们常规写的 RecyclerView.Adapter一样,只是包装了关键方法onCreateViewHolderonBindViewHoldergetItemCount。同时自定义了通用的 BaseViewHolder
  2. 复习了 享元设计模式( Flyweight ) 和 建造者设计模式( Builder )的相关思想(PS:setXXX实现链式调用)
  3. 发现了自己知识的欠缺:关于 反射与泛型的结合使用。

你可能感兴趣的:(BRVAH:基本使用源码分析)