簡單介紹
BRVAH:是由大佬 陳宇明 大佬開源的一款 RecyclerView 的Adapter 框架。官方介紹在此 http://www.recyclerview.org/ 厅缺。首先表示非常感謝與佩服!
廢話開篇
本人由于一直想要深入
RecyclerView
的學(xué)習(xí)與源碼的閱讀(在那么多人的文章帶領(lǐng)下肚了很多源碼都記不住窄刘,無奈面試的時(shí)候一直被問讀了什么源碼舷胜,??)烹骨。而在自己與公司的項(xiàng)目中也一直使用這個(gè)框架沮焕,所以就想要深入學(xué)習(xí)一下這個(gè)框架。這將是一系列文章辣辫,僅僅記錄我閱讀這個(gè)源碼時(shí)的思路與理解急灭。關(guān)于有意義和沒意義這件事谷遂,嗯~~~~~~~我開心就好。所以如果有人不小心看到我的這個(gè)文章扫尖,希望不會辣你的眼睛掠廓。如果你讀完了蟀瞧,我表示感謝悦污。如果你發(fā)現(xiàn)了錯(cuò)誤,歡迎指正
本文思路
- 分析
RecyclerView.Adapter
的基本使用來找出需要關(guān)注的重點(diǎn) - 通過 分析
BRVAH
的基本使用 找出BRVAH
的重點(diǎn) - 按照重點(diǎn)的先后順序逐個(gè)分析
- 總結(jié)
RecyclerView.Adapter
的基本使用
- xml中聲明
RecyclerView
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
-
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<String> getData() {
List<String> 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<DemoAdapter.DemoViewHolder> {
// 很簡單的數(shù)據(jù)項(xiàng)
private List<String> mData;
public DemoAdapter(List<String> data) {
mData = data;
}
// 重點(diǎn)1:創(chuàng)建泛型制定的 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);
}
// 重點(diǎn)2:在這里借助 ViewHodler 操作每一個(gè) position 中的 View
@Override
public void onBindViewHolder(@NonNull DemoViewHolder demoViewHolder, int position) {
demoViewHolder.mTvItem.setText(mData.get(i));
}
// 重點(diǎn)3:返回 item 個(gè)數(shù)
@Override
public int getItemCount() {
return mData == null ? 0 : mData.size();
}
// 重點(diǎn)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);
}
}
}
以上重點(diǎn)1、重點(diǎn)2竞帽、重點(diǎn)3對應(yīng)于 BaseQuickAdapter
鸿捧,重點(diǎn)4對應(yīng)于 BaseViewHolder
將在接下來被著重分析匙奴。首先我們看一下 BRVAH
的基本使用饥脑。
BRVAH 的基本使用
- 創(chuàng)建
BRVAHDemoAdapter
并繼承自BaseQuickAdapter
,并指定數(shù)據(jù)類型為String
,ViewHolder
類型為BaseViewHolder
谣沸。
// 重點(diǎn)5 指定item數(shù)據(jù)類型與 BaseViewHolder
public class BRVAHDemoAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
public BRVAHDemoAdapter(@Nullable List<String> data) {
// 重點(diǎn)6 調(diào)用父類構(gòu)造方法
super(R.layout.demo_adapter, data);
}
// 借助 BaseViewHolder 實(shí)現(xiàn)數(shù)據(jù)與UI的綁定
@Override
protected void convert(BaseViewHolder helper, String item) {
helper.setText(R.id.tv_item, item);
}
}
- 創(chuàng)建
BRVAHDemoAdapter
對象 并替換原來的DemoAdpter
對象賦值給RecyclerView
乳附「吵可以看到實(shí)現(xiàn)了與原來一樣的效果,但是代碼量卻大大地減少荆针,真的是灰常優(yōu)雅了航背。??
通過 RecyclerView.Adapter
的基本使用和 BRVAH
的基本使用對比玖媚,接下來將會按照 重點(diǎn)5—>重點(diǎn)6 —> 重點(diǎn)1 —>重點(diǎn)2—> 重點(diǎn)3 —>重點(diǎn)4 的順序進(jìn)行分析婚脱。
BRVAH 的實(shí)現(xiàn)分析
重點(diǎn)5:BaseQuickAdapter的聲明
public abstract class BaseQuickAdapter<T, K extends BaseViewHolder> extends RecyclerView.Adapter<K>{//...}
public class BaseViewHolder extends RecyclerView.ViewHolder{//...}
可以看到 BaseViewHolder
繼承自RecyclerView.ViewHolder
-
BaseQuickAdapter
的聲明如上所示:可以看到BaseQuickAdapter
是一個(gè)抽象類,繼承自RecyclerView.Adapter
障贸。T
用于表示數(shù)據(jù)項(xiàng)的類型错森;K
表示繼承自BaseViewHolder
的類型。 - 再看
BaseViewHolder
惹想,繼承自RecyclerView.ViewHolder
问词。嗯督函?這不是和我們原來直接繼承RecyclerView.Adapter<T>
一樣了嗎嘀粱???
重點(diǎn)6:BaseQuickAdapter
構(gòu)造方法
三個(gè)重載構(gòu)造方法,最終都調(diào)用BaseQuickAdapter(@LayoutRes int layoutResId, @Nullable List<T> data)
// layoutResId:布局id辰狡;data:數(shù)據(jù)項(xiàng),為空就創(chuàng)建一個(gè)空的 ArrayList
public BaseQuickAdapter(@LayoutRes int layoutResId, @Nullable List<T> data) {
// mData 表示內(nèi)部維護(hù)的數(shù)據(jù)項(xiàng)聲明宛篇,沒有則直接新建一個(gè)空的 ArrayList
this.mData = data == null ? new ArrayList<T>() : data;
if (layoutResId != 0) {
this.mLayoutResId = layoutResId;
}
}
public BaseQuickAdapter(@Nullable List<T> data) {
this(0, data);
}
public BaseQuickAdapter(@LayoutRes int layoutResId) {
this(layoutResId, null);
}
重點(diǎn)1:onCreateViewHolder
該方法返回一個(gè) BaseViewHolder
或其子類娃磺,具體看以下注釋
@Override
public K onCreateViewHolder(ViewGroup parent, int viewType) {
K baseViewHolder = null;
this.mContext = parent.getContext();
// 獲取 LayoutInflater,后續(xù) getItemView 中會用到
this.mLayoutInflater = LayoutInflater.from(mContext);
// 根據(jù)不同的 viewType 創(chuàng)建不同的 ViewHolder
switch (viewType) {
// ...先忽略這一部分
default:
// 分析1:創(chuàng)建默認(rèn)的 ViewHolder
baseViewHolder = onCreateDefViewHolder(parent, viewType);
// 綁定監(jiān)聽事件 先忽略
bindViewClickListener(baseViewHolder);
}
// 傳入當(dāng)前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:調(diào)用 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 根據(jù) layoutResId 返回一個(gè) View
// 然后將這個(gè)View 傳遞給 ViewHolder叫倍。這和我們在 DemoAdapter#onCreateViewHolder中做的不是一樣要到嘛
protected View getItemView(@LayoutRes int layoutResId, ViewGroup parent) {
return mLayoutInflater.inflate(layoutResId, parent, false);
}
// 分析4:根據(jù)泛型和反射獲取 K 的實(shí)際類型
protected K createBaseViewHolder(View view) {
Class temp = getClass();
Class z = null;
while (z == null && null != temp) {
// 獲取 K 的實(shí)際類型
z = getInstancedGenericKClass(temp);
temp = temp.getSuperclass();
}
K k;
// 泛型擦除會導(dǎo)致z為null
if (z == null) {
k = (K) new BaseViewHolder(view);
} else {
// 使用反射 根據(jù) K 的實(shí)際類型創(chuàng)建對象
k = createGenericKInstance(z, view);
}
return k != null ? k : (K) new BaseViewHolder(view);
}
// 獲取泛型類型
private Class getInstancedGenericKClass(Class z) {
// 獲取當(dāng)前父類的范型類型
Type type = z.getGenericSuperclass();
// 父類是否包含泛型
if (type instanceof ParameterizedType) {
// 獲取泛型數(shù)組<T, K extends BaseViewHolder>
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
// 遍歷泛型數(shù)組
for (Type temp : types) {
// 如果這個(gè)泛型是一個(gè)類
if (temp instanceof Class) {
Class tempClass = (Class) temp;
// isAssignableFrom 表示 tempClass 是否是 BaseViewHolder 的子類或者接口
if (BaseViewHolder.class.isAssignableFrom(tempClass)) {
return tempClass;
}
} else if (temp instanceof ParameterizedType) {
// 如果這個(gè)泛型是一個(gè)泛型參數(shù)偷卧,那么就獲取他的實(shí)際類型
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;
// 如果是內(nèi)部類,且不是靜態(tài)類
if (z.isMemberClass() && !Modifier.isStatic(z.getModifiers())) {
// 獲取指定的吆倦、不包括繼承的構(gòu)造函數(shù)
constructor = z.getDeclaredConstructor(getClass(), View.class);
// 屏蔽Java 語言的訪問檢查听诸,使得構(gòu)造函數(shù)可以訪問
constructor.setAccessible(true);
// 為什么要傳入 this,不是特別明白
return (K) constructor.newInstance(this, view);
} else {
// 同上,不再重復(fù)說明
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
內(nèi)部重寫了 onCreateViewHolder
然后通過反射或者 new
的方式返回一個(gè) BaseViewHolder
蚕泽。
重點(diǎn)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()) 用于獲取當(dāng)前
// position對應(yīng)的 data 數(shù)據(jù),當(dāng)前 position 需要去除頭部個(gè)數(shù)(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 對應(yīng)的 data
public T getItem(@IntRange(from = 0) int position) {
// 對 position 做安全范圍判斷
if (position >= 0 && position < mData.size())
return mData.get(position);
else
return null;
}
// 分析3: 子類通過重寫父類的這個(gè)方法,拿到 BaseViewHolder 和Item數(shù)據(jù)
// 并在這個(gè)方法中對 Item 項(xiàng)實(shí)現(xiàn)數(shù)據(jù)操作
protected abstract void convert(K helper, T item);
從以上代碼可以看出:BaseQuickAdapter
通過重寫 onBindViewHolder
方法仔蝌,對于一般情況泛领,通過 convert
抽象方法向子類暴露了 BaseViewHolder
和 當(dāng)前位置的 數(shù)據(jù)項(xiàng) 以供開發(fā)者使用。
重點(diǎn)3:getItemCount
區(qū)別于我們自定義的 Adapter
敛惊,在計(jì)算 item
個(gè)數(shù)的時(shí)候渊鞋,需要考慮 EmptyView
、Header
和Footer
的情況
@Override
public int getItemCount() {
int count;
// 是否存在 EmptyView瞧挤。后續(xù)分析
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 {
// 數(shù)量等于 (1/0) + 數(shù)據(jù)項(xiàng) + (1/0) + (1/0)
count = getHeaderLayoutCount() + mData.size() + getFooterLayoutCount() + getLoadMoreViewCount();
}
return count;
}
在重點(diǎn)2中我們看到,最終生成了一個(gè) BaseViewHolder
對象并在重點(diǎn) 3 中通過 convert
方法向子類暴露用來操作 Item
的UI皿伺。接下來我們就看看 BaseViewHolder
內(nèi)部都做了什么员辩。
重點(diǎn)4:BaseViewHolder
BaseViewHolder
最大的特點(diǎn)就是通過一個(gè)泛型方法 getView
和一系列 setXXX
方法。使得代碼的調(diào)用更加簡潔鸵鸥。
以上方法大都類似奠滑,選一個(gè)我們最常用的設(shè)置文字方法看一下:
public BaseViewHolder setText(@IdRes int viewId, CharSequence value) {
// 分析1 getView 獲取 TextView 實(shí)例
// 并設(shè)置文字
TextView view = getView(viewId);
view.setText(value);
return this;
}
//分析1
public <T extends View> T getView(@IdRes int viewId) {
// views 是一個(gè) SparseArray<View> 對象
// 以下代碼達(dá)到僅在第一此需要某個(gè) View 時(shí)通過 findViewById 獲取
// 以后都從 views 通過 id 獲取
// 這是 Flyweight 設(shè)計(jì)模式的思想嘛
View view = views.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
總結(jié)
基本用法的分析就結(jié)束啦,接下來是總結(jié)妒穴。通過以上的分析宋税,我有以下的收獲:
- 明白了 BRVAH 基本實(shí)現(xiàn)原理,其本質(zhì)和我們常規(guī)寫的
RecyclerView.Adapter
一樣讼油,只是包裝了關(guān)鍵方法onCreateViewHolder
杰赛、onBindViewHolder
、getItemCount
矮台。同時(shí)自定義了通用的BaseViewHolder
- 復(fù)習(xí)了 享元設(shè)計(jì)模式( Flyweight ) 和 建造者設(shè)計(jì)模式( Builder )的相關(guān)思想(PS:setXXX實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用)
- 發(fā)現(xiàn)了自己知識的欠缺:關(guān)于 反射與泛型的結(jié)合使用乏屯。