添加头部和尾部的需求,在 Android 开发中特别常见,那么有什么优雅的方法来完成这个工作了?

0X0000 怎么为优雅的方式

通常我们在添加 Header 与 Footer 的时候,需要在 RecyclerView 的 Adapter 中添加相应的逻辑来实现我们的功能。分别需要在 getViewType, onCreateViewHolder 等方法中针对 header 与 footer 进行修改。但这样会破坏原有的结构,需要在 adapter 中进行代码修改。进一步说,可能还会破坏代码集成结构,强行加入一层继承层次。

优雅的方式,应该是使用组合的,将对 header 与 footer 的支持能力,通过注入(组合)等方式进行,不需要对原有代码进行修改。

早些年,写过一篇文章,关于如何写出更具有扩展性,更为优雅的实现方式。有兴趣的同学,可以参考下。http://www.woaitqs.cc/program/2015/07/30/programming-by-combining.

0X0001 可插拔的HeaderFooterHelper

http headers

在某些情况下,一个客户不想或者不能直接引用一个对 象,此时可以通过一个称之为“代理”的第三者来实现 间接引用。代理对象可以在客户端和目标对象之间起到 中介的作用,并且可以通过代理对象去掉客户不能看到 的内容和服务或者添加客户需要的额外服务。

想要实现得比较优雅,就需要借鉴 Proxy Pattern 模式的思想了。抽象出一个代理,代理只负责将与 header、footer 相关的逻辑,其他功能则路由给被代理的对象。

1, No need change anyting with your target adapter

不需要修改Target Adapter

2, Not destroy target adapter position

不会破坏Target adapter中的 position (PS: 不需要 +1 -1)

3, Support dynamic add & remove

支持动态添加移除

5, No dependencies code build order

不依赖RecyclerView设置顺序 (eg: 不需要提前设置LayoutManager)

1
2
3
4
5
6
7
8
9
10
headerFooterHelper = new HeaderFooterHelper<>(pageAdapter,
new HeaderFooterHelper.HeaderFooterHolderCreator<RippleViewHolder>() {
@Override
public RippleViewHolder generateHolder(View itemView) {
return new RippleViewHolder(itemView);
}
});
recyclerView.setAdapter(headerFooterHelper.getWrapperAdapter());
headerFooterHelper.setFooter(footerView);

0X0002 源码实现

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
import java.util.List;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
/**
* 这个类用于在不破坏原有 Adapter 的基础上,实现添加(删除) header(footer) 的功能。
*
* usage:
*
* <pre>
* <code>
* HeaderFooterHelper helper = new HeaderFooterHelper(baseAdapter, creator);
* recyclerView.setAdapter(helper.getWrapperAdapter());
* </code>
* </pre>
*
* @since 2017/4/7.
* @author qisen.tqs@alibaba-inc.com.
*/
public class HeaderFooterHelper<T extends RecyclerView.ViewHolder> {
// TODO. currently only support one header and one footer.
// TODO. support list of footer views.
// TODO. support set presenter to header and footer.
private View headerView;
private View footerView;
private WrapperAdapter wrapperAdapter;
public void setHeader(View header) {
if (header == null) {
return;
}
this.headerView = header;
wrapperAdapter.notifyDataSetChanged();
}
public void setFooter(View footer) {
if (footer == null) {
return;
}
this.footerView = footer;
wrapperAdapter.notifyDataSetChanged();
}
public void removeFooter() {
headerView = null;
wrapperAdapter.notifyDataSetChanged();
}
public void removeHeader() {
headerView = null;
wrapperAdapter.notifyDataSetChanged();
}
public HeaderFooterHelper(
RecyclerView.Adapter<T> baseAdapter, HeaderFooterHolderCreator<T> creator) {
wrapperAdapter = new WrapperAdapter(baseAdapter, creator);
}
public WrapperAdapter getWrapperAdapter() {
return wrapperAdapter;
}
public interface HeaderFooterHolderCreator<T> {
T generateHolder(View itemView);
}
/**
* proxy pageAdapter used to add header and footer.
*/
private class WrapperAdapter extends RecyclerView.Adapter<T> {
private static final int VIEW_TYPE_HEADER = 1;
private static final int VIEW_TYPE_FOOTER = 2;
/**
* 给 baseAdapter 提供的一个缓冲区,baseAdapter 的
* viewType将左移 VIEW_TYPE_BASIC_OFFSET 这么多位,这样避免header(footer)的viewType 与之冲突。
*/
private static final int VIEW_TYPE_BASIC_OFFSET = 3;
private final RecyclerView.Adapter<T> baseAdapter;
private final HeaderFooterHolderCreator<T> creator;
private WrapperAdapter(RecyclerView.Adapter<T> baseAdapter,
HeaderFooterHolderCreator<T> creator) {
this.creator = creator;
assert baseAdapter != null;
this.baseAdapter = baseAdapter;
}
private boolean isHeader(int position) {
return headerView != null && position == 0;
}
private boolean isFooter(int position) {
return footerView != null && position == getItemCount() - 1;
}
@Override
public void onBindViewHolder(T holder, int position, List<Object> payloads) {
baseAdapter.onBindViewHolder(holder, headerView != null ? position - 1 : position,
payloads);
}
@Override
public int getItemViewType(int position) {
if (isHeader(position)) {
// 设置了 headerView 的情况.
return VIEW_TYPE_HEADER;
} else if (isFooter(position)) {
// 设置了 footerView 的情况.
return VIEW_TYPE_FOOTER;
}
return baseAdapter.getItemViewType(headerView != null ? position - 1 : position);
}
@Override
public void setHasStableIds(boolean hasStableIds) {
baseAdapter.setHasStableIds(hasStableIds);
}
@Override
public long getItemId(int position) {
if (isHeader(position) || isFooter(position)) {
return RecyclerView.NO_ID;
} else {
return baseAdapter.getItemId(headerView != null ? position - 1 : position);
}
}
@Override
public void onViewRecycled(T holder) {
baseAdapter.onViewRecycled(holder);
}
@Override
public boolean onFailedToRecycleView(T holder) {
return baseAdapter.onFailedToRecycleView(holder);
}
@Override
public void onViewAttachedToWindow(T holder) {
baseAdapter.onViewAttachedToWindow(holder);
}
@Override
public void onViewDetachedFromWindow(T holder) {
baseAdapter.onViewDetachedFromWindow(holder);
}
@Override
public void registerAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
baseAdapter.registerAdapterDataObserver(observer);
}
@Override
public void unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver observer) {
baseAdapter.unregisterAdapterDataObserver(observer);
}
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
baseAdapter.onAttachedToRecyclerView(recyclerView);
}
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
baseAdapter.onDetachedFromRecyclerView(recyclerView);
}
@Override
public T onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return creator.generateHolder(headerView);
} else if (viewType == VIEW_TYPE_FOOTER) {
return creator.generateHolder(footerView);
} else {
return baseAdapter.onCreateViewHolder(parent, viewType);
}
}
@Override
public void onBindViewHolder(T holder, int position) {
// not support bind logic to header and footer view.
if (!isHeader(position) && !isFooter(position)) {
baseAdapter.onBindViewHolder(holder, headerView != null ? position - 1 : position);
}
}
@Override
public int getItemCount() {
return baseAdapter.getItemCount()
+ (headerView != null ? 1 : 0)
+ (footerView != null ? 1 : 0);
}
}
}

文档信息