1、目的:为RecyclerView添加头部和底部视图
2、分析:RecyclerView在使用的过程中,没有发现想ListView中addHeadView()、addFooterView()的方法
3、实现思路:模仿ListView的方式实现,研究ListView的添加头部和底部视图的实现源码,依照同样的模式实现
从如下的方法中查看关键信息,会看到mHeaderViewInfos.add(info);就是将传入的参数view通过mHeaderViewInfos进行存储,存放要添加的headerview
接着看到一个判断语句 if (!(mAdapter instanceof HeaderViewListAdapter)),这个判断中有一个方法wrapHeaderListAdapterInternal();进去看到的是mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
意思是如果这个mAdapte不是HeaderViewListAdapter,那么就把它转化成一个HeaderViewListAdapter,同时把这个原本的mAdapter通过构造方法传递到HeaderViewListAdapter这个类中,传入adapter的方式肯定是setAdapter方法,接着看setAdapter
addHeaderView源码:
1public void addHeaderView(View v, Object data, boolean isSelectable) {
2 if (v.getParent() != null && v.getParent() != this) {
3 if (Log.isLoggable(TAG, Log.WARN)) {
4 Log.w(TAG, "The specified child already has a parent. "
5 + "You must call removeView() on the child's parent first.");
6 }
7 }
8 final FixedViewInfo info = new FixedViewInfo();
9 info.view = v;
10 info.data = data;
11 info.isSelectable = isSelectable;
12 mHeaderViewInfos.add(info);
13 mAreAllItemsSelectable &= isSelectable;
14 // Wrap the adapter if it wasn't already wrapped.
15 if (mAdapter != null) {
16 if (!(mAdapter instanceof HeaderViewListAdapter)) {
17 wrapHeaderListAdapterInternal();
18 }
19 // In the case of re-adding a header view, or adding one later on,
20 // we need to notify the observer.
21 if (mDataSetObserver != null) {
22 mDataSetObserver.onChanged();
23 }
24 }
25}
26protected void wrapHeaderListAdapterInternal() {
27 mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
28}
从这个方法看到如下关键代码块:
1if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
2 mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
3 } else {
4 mAdapter = adapter;
5}
就是说setAdapter会先去判断是否你前面添加过headerView或者添加过footerView,以此来判断应该给你转化为一个HeaderViewListAdapter还是直接给你 mAdapter = adapter。
这里体现了偷梁换柱的概念,就是说你添加了headerView或者footerView就用一个新的Adapter来替代你传入的adapter,这个在设计模式上使用了装饰设计模式,接下来查看一下HeaderViewListAdapter
setAdapter源码:
1@Override
2public void setAdapter(ListAdapter adapter) {
3 if (mAdapter != null && mDataSetObserver != null) {
4 mAdapter.unregisterDataSetObserver(mDataSetObserver);
5 }
6 resetList();
7 mRecycler.clear();
8 if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
9 mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
10 } else {
11 mAdapter = adapter;
12 }
13 mOldSelectedPosition = INVALID_POSITION;
14 mOldSelectedRowId = INVALID_ROW_ID;
15 // AbsListView#setAdapter will update choice mode states.
16 super.setAdapter(adapter);
17 if (mAdapter != null) {
18 mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
19 mOldItemCount = mItemCount;
20 mItemCount = mAdapter.getCount();
21 checkFocus();
22 mDataSetObserver = new AdapterDataSetObserver();
23 mAdapter.registerDataSetObserver(mDataSetObserver);
24 mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
25 int position;
26 if (mStackFromBottom) {
27 position = lookForSelectablePosition(mItemCount - 1, false);
28 } else {
29 position = lookForSelectablePosition(0, true);
30 }
31 setSelectedPositionInt(position);
32 setNextSelectedPositionInt(position);
33 if (mItemCount == 0) {
34 // Nothing selected
35 checkSelectionChanged();
36 }
37 } else {
38 mAreAllItemsSelectable = true;
39 checkFocus();
40 // Nothing selected
41 checkSelectionChanged();
42 }
43 requestLayout();
44}
分析看到getView()方法有三处判断,分别是返回了三种视图,头部视图、内容视图、底部视图;发现头部视图和底部视图专门处理,内容视图直接复用了传入的adapter的getView()方法。
1public View getView(int position, View convertView, ViewGroup parent) {
2 // Header (negative positions will throw an IndexOutOfBoundsException)
3 int numHeaders = getHeadersCount();
4 if (position < numHeaders) {
5 return mHeaderViewInfos.get(position).view;
6 }
7 // Adapter
8 final int adjPosition = position - numHeaders;
9 int adapterCount = 0;
10 if (mAdapter != null) {
11 adapterCount = mAdapter.getCount();
12 if (adjPosition < adapterCount) {
13 return mAdapter.getView(adjPosition, convertView, parent);
14 }
15 }
16 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
17 return mFooterViewInfos.get(adjPosition - adapterCount).view;
18}
19public int getItemViewType(int position) {
20 int numHeaders = getHeadersCount();
21 if (mAdapter != null && position >= numHeaders) {
22 int adjPosition = position - numHeaders;
23 int adapterCount = mAdapter.getCount();
24 if (adjPosition < adapterCount) {
25 return mAdapter.getItemViewType(adjPosition);
26 }
27 }
28 return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
29}
综上分析,要实现RecyleView添加头部和底部功能,
1、需要自定义一个RecyleView,添加addHeaerView()、addFooterView()
2、其次自定义一个HeaderViewRecyleViewAdapter,用于实现添加头部和底部视图的展示
1public class WrapRecyleView extends RecyclerView {
2 private ArrayList mHeaderViewInfos = new ArrayList<>();
3 private ArrayList mFooterViewInfos = new ArrayList<>();
4 private Adapter mAdapter;
5 public WrapRecyleView(Context context,AttributeSet attrs) {
6 super(context, attrs);
7 }
8 public void addHeaderView(View v) {
9 mHeaderViewInfos.add(v);
10 // Wrap the adapter if it wasn't already wrapped.
11 if (mAdapter != null) {
12 if (!(mAdapter instanceof HeaderViewRecyleViewAdapter)) {
13 mAdapter = new HeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
14 }
15 }
16 }
17 public void addFooterView(View v) {
18 mFooterViewInfos.add(v);
19 // Wrap the adapter if it wasn't already wrapped.
20 if (mAdapter != null) {
21 if (!(mAdapter instanceof HeaderViewRecyleViewAdapter)) {
22 mAdapter = new HeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
23 }
24 }
25 }
26 @Override
27 public void setAdapter(Adapter adapter) {
28 if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
29 mAdapter = new HeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
30 } else {
31 mAdapter = adapter;
32 }
33 super.setAdapter(mAdapter);
34 }
35}
1public class HeaderViewRecyleViewAdapter extends RecyclerView.Adapter {
2 private ArrayList mHeaderViewInfos;
3 private ArrayList mFooterViewInfos;
4 private RecyclerView.Adapter mAdapter;
5 public HeaderViewRecyleViewAdapter(ArrayList headerViewInfos, ArrayList footerViewInfos, RecyclerView.Adapter adapter) {
6 mAdapter = adapter;
7 if (headerViewInfos == null) {
8 mHeaderViewInfos = new ArrayList<>();
9 } else {
10 mHeaderViewInfos = headerViewInfos;
11 }
12 if (footerViewInfos == null) {
13 mFooterViewInfos = new ArrayList<>();
14 } else {
15 mFooterViewInfos = footerViewInfos;
16 }
17 }
18 @NonNull
19 @Override
20 public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
21 //header
22 if(viewType==RecyclerView.INVALID_TYPE){
23 return new HeaderViewHolder(mHeaderViewInfos.get(0));
24 }else if(viewType==RecyclerView.INVALID_TYPE-1){//footer
25 return new HeaderViewHolder(mFooterViewInfos.get(0));
26 }
27 // Footer (off-limits positions will throw an IndexOutOfBoundsException)
28 return mAdapter.onCreateViewHolder(parent, viewType);
29 }
30 @Override
31 public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
32 //划分头部、正常部分、尾部
33 int numHeaders = getHeadersCount();
34 //头部
35 if (position < numHeaders) {
36 return ;
37 }
38 //adapter bod正常部分
39 final int adjPosition = position - numHeaders;
40 int adapterCount = 0;
41 if (mAdapter != null) {
42 adapterCount = mAdapter.getItemCount();
43 if (adjPosition < adapterCount) {
44 mAdapter.onBindViewHolder(holder, adjPosition);
45 return ;
46 }
47 }
48 //footer
49 }
50 @Override
51 public int getItemCount() {
52 if (mAdapter != null) {
53 return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();
54 } else {
55 return getFootersCount() + getHeadersCount();
56 }
57 }
58 @Override
59 public int getItemViewType(int position) {
60 //判断当前条目是什么类型,就渲染什么视图给什么数据
61 int numHeaders = getHeadersCount();
62 //头部
63 if (position < numHeaders) {
64 return RecyclerView.INVALID_TYPE;
65 }
66 //正常条目部分
67 // Adapter
68 final int adjPosition = position - numHeaders;
69 int adapterCount = 0;
70 if (mAdapter != null) {
71 adapterCount = mAdapter.getItemCount();
72 if (adjPosition < adapterCount) {
73 return mAdapter.getItemViewType(adjPosition);
74 }
75 }
76 //footer部分
77 return RecyclerView.INVALID_TYPE-1;
78 }
79 private static class HeaderViewHolder extends RecyclerView.ViewHolder {
80 public HeaderViewHolder(View view) {
81 super(view);
82 }
83 }
84 public int getHeadersCount() {
85 return mHeaderViewInfos.size();
86 }
87 public int getFootersCount() {
88 return mFooterViewInfos.size();
89 }
90}
91接下来就是应用,需要使用WrapRecyleView 来代替RecyleView ,其它都是正常的实现
92setContentView(R.layout.activity_rvheader);
93recyclerView = (WrapRecyleView) findViewById(R.id.recyclerView);
94addHeaderView();
95addFooterView();
96List list = new ArrayList<>();
97for (int i = 0; i < 6; i++) {
98 list.add("item "+i);
99}
100MyAdapter adapter = new MyAdapter(list);
101recyclerView.setLayoutManager(new LinearLayoutManager(this));
102recyclerView.setAdapter(adapter);
效果图如下:
源码地址:https://github.com/heiyl/recyleview
微信公众号: