简单介绍
BRVAH:是由大佬 陈宇明 大佬开源的一款 RecyclerView 的Adapter 框架。官方介绍在此 http://www.recyclerview.org/ 。首先表示非常感谢与佩服!
废话开篇
本人由于一直想要深入
RecyclerView
的学习与源码的阅读(在那么多人的文章带领下肚了很多源码都记不住,无奈面试的时候一直被问读了什么源码,)。而在自己与公司的项目中也一直使用这个框架,所以就想要深入学习一下这个框架。这将是一系列文章,仅仅记录我阅读这个源码时的思路与理解。关于有意义和没意义这件事,嗯~~~~~~~我开心就好。所以如果有人不小心看到我的这个文章,希望不会辣你的眼睛。如果你读完了,我表示感谢。如果你发现了错误,欢迎指正
本文思路
- 分析
RecyclerView.Adapter
的基本使用来找出需要关注的重点 - 通过 分析
BRVAH
的基本使用 找出BRVAH
的重点 - 按照重点的先后顺序逐个分析
- 总结
RecyclerView.Adapter
的基本使用
- xml中声明
RecyclerView
-
Activity
通过setAdapter
为RecyclerView
赋值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;
}
- 接下来看
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 的基本使用
- 创建
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);
}
}
- 创建
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
-
BaseQuickAdapter
的声明如上所示:可以看到BaseQuickAdapter
是一个抽象类,继承自RecyclerView.Adapter
。T
用于表示数据项的类型;K
表示继承自BaseViewHolder
的类型。 - 再看
BaseViewHolder
,继承自RecyclerView.ViewHolder
。嗯?这不是和我们原来直接继承RecyclerView.Adapter
一样了吗?
重点6:BaseQuickAdapter
构造方法
三个重载构造方法,最终都调用BaseQuickAdapter(@LayoutRes int layoutResId, @Nullable List
// 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
个数的时候,需要考虑 EmptyView
、Header
和Footer
的情况
@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
方法。使得代码的调用更加简洁。
以上方法大都类似,选一个我们最常用的设置文字方法看一下:
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;
}
总结
基本用法的分析就结束啦,接下来是总结。通过以上的分析,我有以下的收获:
- 明白了 BRVAH 基本实现原理,其本质和我们常规写的
RecyclerView.Adapter
一样,只是包装了关键方法onCreateViewHolder
、onBindViewHolder
、getItemCount
。同时自定义了通用的BaseViewHolder
- 复习了 享元设计模式( Flyweight ) 和 建造者设计模式( Builder )的相关思想(PS:setXXX实现链式调用)
- 发现了自己知识的欠缺:关于 反射与泛型的结合使用。