banner
NEWS LETTER

打造自己的RecyclerView适配器和可添加头部尾部的RecyclerView

Scroll down

打造自己的RecyclerView通用适配器,使用更方便

创建多布局支持接口MultiTypeSupport

1
2
3
4
5
6
7
8
9
10
public interface MultiTypeSupport<T> {
/**
* 根据当前位置或者条目数据返回布局
*
* @param item T
* @param position int
* @return int
*/
int getLayoutId(T item, int position);
}

创建Adapter条目的点击事件接口

1
2
3
4
5
6
7
public interface OnItemClickListener {
/**
* 点击
* @param position int
*/
void onItemClick(int position);
}

创建Adapter条目的长按事件接口

1
2
3
4
5
6
7
8
9
public interface OnItemLongClickListener {
/**
* 长按
*
* @param position i
* @return b
*/
boolean onLongClick(int position);
}

创建RecyclerView的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
public class ViewHolder extends RecyclerView.ViewHolder {

/**
* 用来存放子View减少findViewById的次数
*/
private final SparseArray<View> mViews;

public ViewHolder(View itemView) {
super(itemView);
mViews = new SparseArray<>();
}

/**
* 设置TextView文本
*/
public ViewHolder setText(int viewId, CharSequence text) {
TextView tv = getView(viewId);
tv.setText(text);
return this;
}

/**
* 通过id获取view
*/
public <T extends View> T getView(int viewId) {
// 先从缓存中找
View view = mViews.get(viewId);
if (view == null) {
// 直接从ItemView中找
view = itemView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}

/**
* 设置View的Visibility
*/
public ViewHolder setViewVisibility(int viewId, int visibility) {
getView(viewId).setVisibility(visibility);
return this;
}

/**
* 设置ImageView的资源
*/
public ViewHolder setImageResource(int viewId, int resourceId) {
ImageView imageView = getView(viewId);
imageView.setImageResource(resourceId);
return this;
}

/**
* 设置条目点击事件
*/
public void setOnItemClickListener(View.OnClickListener listener) {
itemView.setOnClickListener(listener);
}

/**
* 设置条目长按事件
*/
public void setOnItemLongClickListener(View.OnLongClickListener listener) {
itemView.setOnLongClickListener(listener);
}

/**
* 设置图片通过路径,这里稍微处理得复杂一些,因为考虑加载图片的第三方可能不太一样
* 也可以直接写死
*/
public ViewHolder setImageByUrl(int viewId, HolderImageLoader imageLoader) {
ImageView imageView = getView(viewId);
if (imageLoader == null) {
throw new NullPointerException("imageLoader is null!");
}
imageLoader.displayImage(imageView.getContext(), imageView, imageLoader.getImagePath());
return this;
}

/**
* 图片加载,这里稍微处理得复杂一些,因为考虑加载图片的第三方可能不太一样
* 也可以不写这个类
*/
public static abstract class HolderImageLoader {
private final String mImagePath;

public HolderImageLoader(String imagePath) {
this.mImagePath = imagePath;
}

public String getImagePath() {
return mImagePath;
}

public abstract void displayImage(Context context, ImageView imageView, String imagePath);
}
}

创建通用的Adapter

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
public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<ViewHolder> {

protected Context mContext;
protected LayoutInflater mInflater;
/**
* 数据怎么办?
*/
protected List<T> mData;
/**
* 布局怎么办?
*/
private int mLayoutId;

// 多布局支持
private MultiTypeSupport mMultiTypeSupport;

public BaseRecyclerAdapter(Context context, List<T> data, int layoutId) {
this.mContext = context;
this.mInflater = LayoutInflater.from(mContext);
this.mData = data;
this.mLayoutId = layoutId;
}

/**
* 多布局支持
*/
public BaseRecyclerAdapter(Context context, List<T> data, MultiTypeSupport<T> multiTypeSupport) {
this(context, data, -1);
this.mMultiTypeSupport = multiTypeSupport;
}

/**
* 根据当前位置获取不同的viewType
*/
@Override
public int getItemViewType(int position) {
// 多布局支持
if (mMultiTypeSupport != null) {
return mMultiTypeSupport.getLayoutId(mData.get(position), position);
}
return super.getItemViewType(position);
}

@NotNull
@Override
public ViewHolder onCreateViewHolder(@NotNull ViewGroup parent, int viewType) {
// 多布局支持
if (mMultiTypeSupport != null) {
mLayoutId = viewType;
}
// 先inflate数据
View itemView = mInflater.inflate(mLayoutId, parent, false);
// 返回ViewHolder
return new ViewHolder(itemView);
}

@Override
public void onBindViewHolder(@NotNull final ViewHolder holder, final int position) {
// 设置点击和长按事件
if (mItemClickListener != null) {
holder.itemView.setOnClickListener(v -> mItemClickListener.onItemClick(holder.getAdapterPosition()));
}
if (mLongClickListener != null) {
holder.itemView.setOnLongClickListener(v -> mLongClickListener.onLongClick(holder.getAdapterPosition()));
}

// 绑定怎么办?回传出去
convert(holder, mData.get(position));
}

/**
* 利用抽象方法回传出去,每个不一样的Adapter去设置
* @param holder ViewHolder
* @param item 当前的数据
*/
public abstract void convert(ViewHolder holder, T item);

@Override
public int getItemCount() {
return mData.size();
}

/***************
* 给条目设置点击和长按事件
*********************/
public OnItemClickListener mItemClickListener;
public OnItemLongClickListener mLongClickListener;

public void setOnItemClickListener(OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener;
}

public void setOnLongClickListener(OnItemLongClickListener longClickListener) {
this.mLongClickListener = longClickListener;
}
}

创建可添加头部和尾部的RecyclerView

先创建WrapRecyclerAdapter适配器

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
204
205
206
207
208
209
210
211
212
213
214
215
public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final static String TAG = "WrapRecyclerAdapter";
// 用来存放底部和头部View的集合 比Map要高效一些
// 可以点击进入看一下官方的解释
/**
* SparseArrays map integers to Objects. Unlike a normal array of Objects,
* there can be gaps in the indices. It is intended to be more memory efficient
* than using a HashMap to map Integers to Objects, both because it avoids
* auto-boxing keys and its data structure doesn't rely on an extra entry object
* for each mapping.
*/
private final SparseArray<View> mHeaderViews;
private final SparseArray<View> mFooterViews;

/**
* 基本的头部类型开始位置 用于viewType
*/
private static int BASE_ITEM_TYPE_HEADER = 10000000;
/**
* 基本的底部类型开始位置 用于viewType
*/
private static int BASE_ITEM_TYPE_FOOTER = 20000000;

/**
* 列表的Adapter
*/
private final RecyclerView.Adapter mAdapter;

public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
mHeaderViews = new SparseArray<>();
mFooterViews = new SparseArray<>();
}

@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// viewType 可能就是 SparseArray 的key
if (isHeaderViewType(viewType)) {
View headerView = mHeaderViews.get(viewType);
return createHeaderFooterViewHolder(headerView);
}
if (isFooterViewType(viewType)) {
View footerView = mFooterViews.get(viewType);
return createHeaderFooterViewHolder(footerView);
}
return mAdapter.onCreateViewHolder(parent, viewType);
}

@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (isHeaderPosition(position) || isFooterPosition(position)) {
return;
}
// 计算一下位置
final int adapterPosition = position - mHeaderViews.size();
mAdapter.onBindViewHolder(holder, adapterPosition);

// 设置点击和长按事件
if (mItemClickListener != null) {
holder.itemView.setOnClickListener(v -> mItemClickListener.onItemClick(adapterPosition));
}
if (mLongClickListener != null) {
holder.itemView.setOnLongClickListener(v -> mLongClickListener.onLongClick(adapterPosition));
}
}

@Override
public int getItemCount() {
// 条数三者相加 = 底部条数 + 头部条数 + Adapter的条数
return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
}

/**
* 是不是底部类型
*/
private boolean isFooterViewType(int viewType) {
int position = mFooterViews.indexOfKey(viewType);
return position >= 0;
}

/**
* 是不是底部位置
*/
private boolean isFooterPosition(int position) {
return position >= (mHeaderViews.size() + mAdapter.getItemCount());
}

/**
* 是不是头部位置
*/
private boolean isHeaderPosition(int position) {
return position < mHeaderViews.size();
}

/**
* 创建头部或者底部的ViewHolder
*/
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {
};
}

/**
* 是不是头部类型
*/
private boolean isHeaderViewType(int viewType) {
int position = mHeaderViews.indexOfKey(viewType);
return position >= 0;
}

@Override
public int getItemViewType(int position) {
if (isHeaderViewType(position)) {
// 直接返回position位置的key
return mHeaderViews.keyAt(position);
}
if (isFooterPosition(position)) {
// 直接返回position位置的key
position = position - mHeaderViews.size() - mAdapter.getItemCount();
return mFooterViews.keyAt(position);
}
// 返回列表Adapter的getItemViewType
position = position - mHeaderViews.size();
return mAdapter.getItemViewType(position);
}
/**
* 获取列表的Adapter
*/
private RecyclerView.Adapter getAdapter() {
return mAdapter;
}

/**
* 添加头部
*/
public void addHeaderView(View view) {
int position = mHeaderViews.indexOfValue(view);
if (position < 0) {
mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
}
notifyDataSetChanged();
}

/**
* 添加底部
*/
public void addFooterView(View view) {
int position = mFooterViews.indexOfValue(view);
if (position < 0) {
mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view);
}
notifyDataSetChanged();
}

/**
* 移除头部
*/
public void removeHeaderView(View view) {
int index = mHeaderViews.indexOfValue(view);
if (index < 0) {
return;
}
mHeaderViews.removeAt(index);
notifyDataSetChanged();
}

/**
* 移除底部
*/
public void removeFooterView(View view) {
int index = mFooterViews.indexOfValue(view);
if (index < 0) {
return;
}
mFooterViews.removeAt(index);
notifyDataSetChanged();
}

/**
* 解决GridLayoutManager添加头部和底部不占用一行的问题
*
* @param recycler
* @version 1.0
*/
public void adjustSpanSize(RecyclerView recycler) {
if (recycler.getLayoutManager() instanceof GridLayoutManager) {
final GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
boolean isHeaderOrFooter =
isHeaderPosition(position) || isFooterPosition(position);
return isHeaderOrFooter ? layoutManager.getSpanCount() : 1;
}
});
}
}

/***************
* 给条目设置点击和长按事件
*********************/
public OnItemClickListener mItemClickListener;
public OnItemLongClickListener mLongClickListener;

public void setOnItemClickListener(OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener;
}

public void setOnLongClickListener(OnItemLongClickListener longClickListener) {
this.mLongClickListener = longClickListener;
}

}

创建可以添加头部和底部的RecyclerView

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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
public class WrapRecyclerView extends RecyclerView {
/**
* 包裹了一层的头部底部Adapter
*/
private WrapRecyclerAdapter mWrapRecyclerAdapter;
/**
* 这个是列表数据的Adapter
*/
private RecyclerView.Adapter mAdapter;

/**
* 增加一些通用功能
* 空列表数据应该显示的空View
* 正在加载数据页面,也就是正在获取后台接口页面
*/
private View mEmptyView, mLoadingView;

public WrapRecyclerView(Context context) {
super(context);
}

public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

@Override
public void setAdapter(Adapter adapter) {
// 为了防止多次设置Adapter
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mDataObserver);
mAdapter = null;
}

this.mAdapter = adapter;

if (adapter instanceof WrapRecyclerAdapter) {
mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
} else {
mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
}

super.setAdapter(mWrapRecyclerAdapter);

// 注册一个观察者
mAdapter.registerAdapterDataObserver(mDataObserver);

// 解决GridLayout添加头部和底部也要占据一行
mWrapRecyclerAdapter.adjustSpanSize(this);

// 加载数据页面
if (mLoadingView != null && mLoadingView.getVisibility() == View.VISIBLE) {
mLoadingView.setVisibility(View.GONE);
}

if (mItemClickListener != null) {
mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
}

if (mLongClickListener != null) {
mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
}
}

/**
* 添加头部
*/
public void addHeaderView(View view) {
// 如果没有Adapter那么就不添加,也可以选择抛异常提示
// 让他必须先设置Adapter然后才能添加,这里是仿照ListView的处理方式
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addHeaderView(view);
}
}

/**
* 添加底部
*/
public void addFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addFooterView(view);
}
}

/**
* 移除头部
*/
public void removeHeaderView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeHeaderView(view);
}
}

/**
* 移除底部
*/
public void removeFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeFooterView(view);
}
}

private final AdapterDataObserver mDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
if (mAdapter == null) {
return;
}
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyDataSetChanged();
}

dataChanged();
}

@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
if (mAdapter == null) {
return;
}
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
}
dataChanged();
}

@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
if (mAdapter == null) {
return;
}
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved没效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
}
dataChanged();
}

@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
if (mAdapter == null) {
return;
}
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemChanged(positionStart);
}
dataChanged();
}

@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (mAdapter == null) {
return;
}
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
}
dataChanged();
}

@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
if (mAdapter == null) {
return;
}
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted没效果
if (mWrapRecyclerAdapter != mAdapter) {
mWrapRecyclerAdapter.notifyItemInserted(positionStart);
}
dataChanged();
}
};

/**
* 添加一个空列表数据页面
*/
public void addEmptyView(View emptyView) {
this.mEmptyView = emptyView;
}

/**
* 添加一个正在加载数据的页面
*/
public void addLoadingView(View loadingView) {
this.mLoadingView = loadingView;
mLoadingView.setVisibility(View.VISIBLE);
}

/**
* Adapter数据改变的方法
*/
private void dataChanged() {
if (mAdapter.getItemCount() == 0) {
// 没有数据
if (mEmptyView != null) {
mEmptyView.setVisibility(VISIBLE);
} else {
mEmptyView.setVisibility(GONE);
}
}
}

/***************
* 给条目设置点击和长按事件
*********************/
public OnItemClickListener mItemClickListener;
public OnItemLongClickListener mLongClickListener;

public void setOnItemClickListener(OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener;

if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
}
}

public void setOnItemLongClickListener(OnItemLongClickListener longClickListener) {
this.mLongClickListener = longClickListener;

if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
}
}
}

使用方法

1
2
3
4

public class ImageListAdapter extends BaseRecyclerAdapter<UserBean> {
}

其他文章
目录导航 置顶
  1. 1. 打造自己的RecyclerView通用适配器,使用更方便
  2. 2. 创建多布局支持接口MultiTypeSupport
  3. 3. 创建Adapter条目的点击事件接口
  4. 4. 创建Adapter条目的长按事件接口
  5. 5. 创建RecyclerView的ViewHolder
  6. 6. 创建通用的Adapter
  7. 7. 创建可添加头部和尾部的RecyclerView
  8. 8. 创建可以添加头部和底部的RecyclerView
  9. 9. 使用方法
请输入关键词进行搜索