MaterialDesign-RecyleView探究及使用【三】-添加头部和底部

概述

1、目的:为RecyclerView添加头部和底部视图
2、分析:RecyclerView在使用的过程中,没有发现想ListView中addHeadView()、addFooterView()的方法
3、实现思路:模仿ListView的方式实现,研究ListView的添加头部和底部视图的实现源码,依照同样的模式实现

ListView添加头部和底部的功能源码解析

1、查看一下ListView的addHeaderView()方法:

从如下的方法中查看关键信息,会看到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}
2、查看一下ListView的setAdapter()方法:

从这个方法看到如下关键代码块:

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}
3、查看HeaderViewListAdapter关键源码:

分析看到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,用于实现添加头部和底部视图的展示

自定义RecyleView实现WrapRecyleView类:

 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}

HeaderViewRecyleViewAdapter类实现:

  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);

效果图如下:

MaterialDesign-RecyleView探究及使用【三】-添加头部和底部_第1张图片效果图如下:

源码地址:https://github.com/heiyl/recyleview

微信公众号:

MaterialDesign-RecyleView探究及使用【三】-添加头部和底部_第2张图片图注:Android进化之路公众号

你可能感兴趣的:(Android进阶学习)