Android RecyclerView ViewHolder
01.ViewHolder的作用
- ViewHolder作用大概有这些:
- adapter应当拥有ViewHolder的子类,并且ViewHolder内部应当存储一些子view,避免时间代价很大的findViewById操作
- 其RecyclerView内部定义的ViewHolder类包含很多复杂的属性,内部使用场景也有很多,而我们经常使用的也就是onCreateViewHolder()方法和onBindViewHolder()方法,onCreateViewHolder()方法在RecyclerView需要一个新类型。item的ViewHolder时调用来创建一个ViewHolder,而onBindViewHolder()方法则当RecyclerView需要在特定位置的item展示数据时调用。
02.ViewHolder与复用
在复写RecyclerView.Adapter的时候,需要我们复写两个方法:
- onCreateViewHolder
- onBindViewHolder
- 这两个方法从字面上看就是创建ViewHolder和绑定ViewHolder的意思
复用机制是怎样的?
- 模拟场景:只有一种ViewType,上下滑动的时候需要的ViewHolder种类是只有一种,但是需要的ViewHolder对象数量并不止一个。所以在后面创建了9个ViewHolder之后,需要的数量够了,无论怎么滑动,都只需要复用以前创建的对象就行了。那么逗比程序员们思考一下,为什么会出现这种情况呢
- 看到了下面log之后,第一反应是在这个ViewHolder对象的数量“够用”之后就停止调用onCreateViewHolder方法,但是onBindViewHolder方法每次都会调用的
查看一下createViewHolder源代码
- 发现这里并没有限制
1
2
3
4
5
6
7public final VH createViewHolder(ViewGroup parent, int viewType) {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
TraceCompat.endSection();
return holder;
}对于ViewHolder对象的数量“够用”之后就停止调用onCreateViewHolder方法,可以查看
- 获取为给定位置初始化的视图。
- 此方法应由{@link LayoutManager}实现使用,以获取视图来表示来自{@LinkAdapter}的数据。
- 如果共享池可用于正确的视图类型,则回收程序可以重用共享池中的废视图或分离视图。如果适配器没有指示给定位置上的数据已更改,则回收程序将尝试发回一个以前为该数据初始化的报废视图,而不进行重新绑定。
1
2
3
4
5
6
7
8
9
10
11
12public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,boolean dryRun, long deadlineNs) {
//代码省略
}
03.ViewHolder简单封装
关于ViewHolder简单的封装代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124public abstract class BaseMViewHolder<M> extends RecyclerView.ViewHolder {
// SparseArray 比 HashMap 更省内存,在某些条件下性能更好,只能存储 key 为 int 类型的数据,
// 用来存放 View 以减少 findViewById 的次数
private SparseArray<View> viewSparseArray;
BaseMViewHolder(View itemView) {
super(itemView);
if(viewSparseArray==null){
viewSparseArray = new SparseArray<>();
}
}
public BaseMViewHolder(ViewGroup parent, @LayoutRes int res) {
super(LayoutInflater.from(parent.getContext()).inflate(res, parent, false));
if(viewSparseArray==null){
viewSparseArray = new SparseArray<>();
}
}
/**
* 子类设置数据方法
* @param data
*/
public void setData(M data) {}
/**
* 第二种findViewById方式
* 根据 ID 来获取 View
* @param viewId viewID
* @param <T> 泛型
* @return 将结果强转为 View 或 View 的子类型
*/
@SuppressWarnings("unchecked")
protected <T extends View> T getView(int viewId) {
// 先从缓存中找,找打的话则直接返回
// 如果找不到则 findViewById ,再把结果存入缓存中
View view = viewSparseArray.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
viewSparseArray.put(viewId, view);
}
return (T) view;
}
/**
* 获取上下文context
* @return context
*/
protected Context getContext(){
return itemView.getContext();
}
/**
* 获取数据索引的位置
* @return position
*/
protected int getDataPosition(){
RecyclerView.Adapter adapter = getOwnerAdapter();
if (adapter!=null && adapter instanceof RecyclerArrayAdapter){
return getAdapterPosition() - ((RecyclerArrayAdapter) adapter).getHeaderCount();
}
return getAdapterPosition();
}
/**
* 获取adapter对象
* @param <T>
* @return adapter
*/
@Nullable
private <T extends RecyclerView.Adapter> T getOwnerAdapter(){
RecyclerView recyclerView = getOwnerRecyclerView();
//noinspection unchecked
return recyclerView != null ? (T) recyclerView.getAdapter() : null;
}
@Nullable
private RecyclerView getOwnerRecyclerView(){
try {
Field field = RecyclerView.ViewHolder.class.getDeclaredField("mOwnerRecyclerView");
field.setAccessible(true);
return (RecyclerView) field.get(this);
} catch (NoSuchFieldException ignored) {
ignored.printStackTrace();
} catch (IllegalAccessException ignored) {
ignored.printStackTrace();
}
return null;
}
/**
* 添加子控件的点击事件
* @param viewId 控件id
*/
protected void addOnClickListener(@IdRes final int viewId) {
final View view = getView(viewId);
if (view != null) {
if (!view.isClickable()) {
view.setClickable(true);
}
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(getOwnerAdapter()!=null){
if (((RecyclerArrayAdapter)getOwnerAdapter()).getOnItemChildClickListener() != null) {
((RecyclerArrayAdapter)getOwnerAdapter()).getOnItemChildClickListener()
.onItemChildClick(v, getDataPosition());
}
}
}
});
}
}
//省略部分代码
}