当前位置:   article > 正文

Lsn5研究ListView源码来封装RecylerView实现添加无数个头布局和尾布局

lsn5

一,效果图和前言
我们知道ListView可以添加多个头布局和尾布局,但是RecylerView没有提供这两个方法。所以今天我们来封装RecylerView来实现和ListView一样可以添加头布局和尾布局。如图下图片所示:
我们可以添加无数个头布局和尾布局。封装的RecylerView当然了必须使用起来和原生一样简单好用。那么我们如何来封装呢?
这里写图片描述

二,看ListView源码来分析如何分装自己的RecylerView:
我们知道ListView可以随便添加头布局和尾布局,那么我们可以根据借鉴ListView来分装自己的RecylerView。
1)我们来分析ListView如何添加头布局:
首先我们在MainActivity里面来下个定义一个ListView listview变量,然后listView.addHeaderView();我们点击addHeaderView():去看源码,如果你没有源码那么你可以下载或者找到你有的API就可以。点击进去代码如下:

   ArrayList<FixedViewInfo> mHeaderViewInfos = Lists.newArrayList();
ArrayList<FixedViewInfo> mFooterViewInfos = Lists.newArrayList();
public void addHeaderView(View v, Object data, boolean isSelectable) {
    final FixedViewInfo info = new FixedViewInfo();
    info.view = v;
    info.data = data;
    info.isSelectable = isSelectable;
    mHeaderViewInfos.add(info);
    mAreAllItemsSelectable &= isSelectable;

    // Wrap the adapter if it wasn't already wrapped.
    if (mAdapter != null) {
        if (!(mAdapter instanceof HeaderViewListAdapter)) {
            wrapHeaderListAdapterInternal();
        }

        // In the case of re-adding a header view, or adding one later on,
        // we need to notify the observer.
        if (mDataSetObserver != null) {
            mDataSetObserver.onChanged();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

我们可以看到ListView源码里面用一个集合mHeaderViewInfos来储存头布局的信息。
然后来判断mAdapter是否为空if (mAdapter != null) {
if (!(mAdapter instanceof HeaderViewListAdapter)) {
mAdapter = HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}

我们这里可以看出ListView用了一个代理HeaderViewListAdapter适配器,来和我们自定义的Adapter交互,实现头布局和尾布局的,这只是一个猜想,我们看看listview.setAdapter()代码如下:

@Override
public void setAdapter(ListAdapter adapter) {
    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
        mAdapter = HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
        mAdapter = adapter;
    }
    super.setAdapter(adapter);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这个是关键代码哦:我们看出来,如果添加了头布局和尾布局那么就去HeaderViewListAdapter处理头布局和尾布局。如果没有头布局和尾布局那么直接给mAdapter赋值为我们自定义的adapter。
接下来我们顺藤摸瓜:点进去HeaderViewListAdapter类看看究竟做了什么:
首先看到:

private final ListAdapter mAdapter;
ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
                             ArrayList<ListView.FixedViewInfo> footerViewInfos,
                             ListAdapter adapter) {
    mAdapter = adapter;
    mIsFilterable = adapter instanceof Filterable;

    if (headerViewInfos == null) {
        mHeaderViewInfos = EMPTY_INFO_LIST;
    } else {
        mHeaderViewInfos = headerViewInfos;
    }

    if (footerViewInfos == null) {
        mFooterViewInfos = EMPTY_INFO_LIST;
    } else {
        mFooterViewInfos = footerViewInfos;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

我们这里可以看到这里获取了总的item个数。为头布局,尾布局加listview的item个数和。

public int getCount() {
    if (mAdapter != null) {
        return getFootersCount() + getHeadersCount() + mAdapter.getCount();
    } else {
        return getFootersCount() + getHeadersCount();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

这里根据position和头尾列表item个数计算条目的类型(头布局条目,列表条目,尾布局条目)返回给对应的数据。

public Object getItem(int position) {
    int numHeaders = getHeadersCount();
  // 这里判断如果是头布局,就返回头布局条目。
    if (position < numHeaders) {
        return mHeaderViewInfos.get(position).data;
    }
    // 列表item的数据。
    final int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getCount();
        if (adjPosition < adapterCount) {
            return mAdapter.getItem(adjPosition);
        }
    }
    // 尾布局条目
    return mFooterViewInfos.get(adjPosition - adapterCount).data;
}
最后来到getView里面代码如下:根据条目数来判断返回的view类型。实现头布局和尾布局的添加。
public View getView(int position, View convertView, ViewGroup parent) {
    // Header (negative positions will throw an IndexOutOfBoundsException)
    int numHeaders = getHeadersCount();
    if (position < numHeaders) {
        return mHeaderViewInfos.get(position).view;
    }

    // Adapter
    final int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getCount();
        if (adjPosition < adapterCount) {
            return mAdapter.getView(adjPosition, convertView, parent);
        }
    }

    // Footer (off-limits positions will throw an IndexOutOfBoundsException)
    return mFooterViewInfos.get(adjPosition - adapterCount).view;
}
  • 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

我们可以看到这个代理HeaderViewListAdapter类,它处理完头布局尾布局和列表item直接的关系,然后调用 return mAdapter.getView(adjPosition, convertView, parent);来和我们自定义的adapter交互。我们现在知道了listView的源码添加头和尾的原理:就是通过一个代理adapter来处理完头布局和尾布局,然后去和我们自定义的MyAdapter交互。接下来我们来分装自己的RecylerView实现listView一样添加头布局和尾布局的方法。

三,分装开始:
1)首先我们来写一个类继承RecylerView,里面写2个集合来存放头布局和尾布局view,
然后写一个addHeaderView方法,和addFooterView方法,以及setAdaper方法,addHeaderView方法根据ListView的addHeaderView方法,我们可以判断是否是有头布局和尾布局如果有那么我们需要自定义一个代理适配器去处理头布局和尾布局。
代码如下:

  public class WrapRecylerView extends RecyclerView {
    private ArrayList<View> mHeaderListArray = new ArrayList<View>();
    private ArrayList<View> mFooterListArray = new ArrayList<View>();
    private Adapter mAdapter;
    public WrapRecylerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public void addHeaderView(View v) {
        mHeaderListArray.add(v);
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewRecylerViewAdapter)) {
                mAdapter=new HeaderViewRecylerViewAdapter(mHeaderListArray, mFooterListArray, mAdapter);
            }
        }
    }

    public void addFooterView(View v) {
        mFooterListArray.add(v);
        // Wrap the adapter if it wasn't already wrapped.
        if (mAdapter != null) {
            if (!(mAdapter instanceof HeaderViewRecylerViewAdapter)) {
               mAdapter= new HeaderViewRecylerViewAdapter(mHeaderListArray, mFooterListArray, mAdapter);

            }
        }
    }

    @Override
    public void setAdapter(Adapter adapter) {
        if (mHeaderListArray.size() > 0 || mFooterListArray.size() > 0) {
            mAdapter = new HeaderViewRecylerViewAdapter(mHeaderListArray, mFooterListArray, adapter);
        } else {
            mAdapter = adapter;
        }
        super.setAdapter(mAdapter);
    }
}
  • 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

2)我们这里来自定义一个代理类HeaderViewRecylerViewAdapter继承RecylerVeiw.Adapter处理头布局和尾布局:
首先我们构造方法需要头布局和尾布局view集合以及 mAdapter代码如下:

public class HeaderViewRecylerViewAdapter extends RecyclerView.Adapter {
    private ArrayList<View> mHeaderListArray;
    private ArrayList<View> mFooterListArray;
    private RecyclerView.Adapter mAdapter;
    private int position;
    public HeaderViewRecylerViewAdapter(ArrayList<View> mHeaderListArray, ArrayList<View> mFooterListArray, RecyclerView.Adapter mAdapter) {
        if (mHeaderListArray == null) {
            this.mHeaderListArray = new ArrayList<View>();
        } else {
            this.mHeaderListArray = mHeaderListArray;
        }
        if (mFooterListArray == null) {
            this.mFooterListArray = new ArrayList<View>();
        } else {
            this.mFooterListArray = mFooterListArray;
        }
        this.mAdapter = mAdapter;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

接下来我们重写getItemCount,onBindViewHolder和onCreateViewHolder方法,以及以下获取布局个数的方法,一个静态内部类继承RecylerView.ViewHolder代码下:

public class HeaderViewRecylerViewAdapter extends RecyclerView.Adapter {
    private ArrayList<View> mHeaderListArray;
    private ArrayList<View> mFooterListArray;
    private RecyclerView.Adapter mAdapter;
    private int position;
    public HeaderViewRecylerViewAdapter(ArrayList<View> mHeaderListArray, ArrayList<View> mFooterListArray, RecyclerView.Adapter mAdapter) {
        if (mHeaderListArray == null) {
            this.mHeaderListArray = new ArrayList<View>();
        } else {
            this.mHeaderListArray = mHeaderListArray;
        }
        if (mFooterListArray == null) {
            this.mFooterListArray = new ArrayList<View>();
        } else {
            this.mFooterListArray = mFooterListArray;
        }
        this.mAdapter = mAdapter;
    }
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

}
    @Override
    public int getItemCount() {

    }

    public int getHeadersCount() {
        return mHeaderListArray.size();
    }

    public int getFootersCount() {
        return mFooterListArray.size();
    }

    public static class HeaderViewHolder extends RecyclerView.ViewHolder {

        public HeaderViewHolder(View itemView) {
            super(itemView);
        }
    }
}
  • 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

我们可以根据listView代理适配器看出,getItemCount()方法返回的是布局条目的个数,我们分为当RecylerView列表没数据时候,和有数据2中返回itemcount代码如下:

@Override
public int getItemCount() {
    if (mAdapter != null) {
        return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();
    } else {
        return getFootersCount() + getHeadersCount();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

然后onCreateViewHolder我们玩过RecylerView的人都知道,他是用来创建对应的ViewHolder的,这里要根据不同的条目类型去创建不同的ViewHolder但是我们如何判断条目类型是头布局条目还是尾布局条目还是RecylerView的条目。我们要重写getItemViewType来确定条目的类型。代码如下:

@Override
public int getItemViewType(int position) {

    // Header (negative positions will throw an IndexOutOfBoundsException)
    int numHeaders = getHeadersCount();
    int numFooters=getFootersCount();
    if (position < numHeaders) {
       //定义成员变量positon来确定条目的位置。这里position是头布局里面的条目
        this.position=position;
        //当然了这里这个返回类型你随便自己定义类。给个666也行,只不过显得有点low我参考listView源码写的。下面同理。
        return RecyclerView.INVALID_TYPE;
    }

    // Adapter
    final int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getItemCount();
        if (adjPosition < adapterCount) {
           //这里是RcylderView上的position从头布局position一下开始算起。
            this.position=adjPosition;
           //返回列表类型。
            return mAdapter.getItemViewType(adjPosition);
        }
    }
   //这里是尾布局里面的position.
    this.position=position-numHeaders-adapterCount;
   //返回尾布局类型给onCreateViewHolder
    // Footer (off-limits positions will throw an IndexOutOfBoundsException)
    return RecyclerView.INVALID_TYPE - 1;
}
  • 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

接下来就是在onCreateViewHolder里面进行创建对应的ViewHolder实例化对应的View。代码如下:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
   //这里判断是头布局条目类型。
    if (viewType == RecyclerView.INVALID_TYPE) {
        return new HeaderViewHolder(mHeaderListArray.get(position));
    } else if (viewType == (RecyclerView.INVALID_TYPE - 1)) {//尾布局类型。
        return new HeaderViewHolder(mFooterListArray.get(position));
    }
   //列表内容部分:
    return mAdapter.onCreateViewHolder(parent, viewType);
}

最后一步我们需要绑定数据到对应的ViewHolder上,这里我们分为三部分,代码如下:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    int numHeaders = getHeadersCount();
    if (position < numHeaders) {
        return;
    }
    //adapter列表部分
    final int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getItemCount();
        if (adjPosition < adapterCount) {
            mAdapter.onBindViewHolder(holder, adjPosition);
            return;
        }
    }

    //尾部和头部不需要处理所以。就不管了。
}
  • 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

到这里我们接写完了代理类。HeaderViewRecylerViewAdapter ,代码如下:

package com.example.ls.lsn5_materialdesign_wraprecylerview.wraprecylerview;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

/**
 * Created by ls on 2017/8/16.
 */

public class HeaderViewRecylerViewAdapter extends RecyclerView.Adapter {
    private ArrayList<View> mHeaderListArray;
    private ArrayList<View> mFooterListArray;
    private RecyclerView.Adapter mAdapter;
    private int position;
    public HeaderViewRecylerViewAdapter(ArrayList<View> mHeaderListArray, ArrayList<View> mFooterListArray, RecyclerView.Adapter mAdapter) {
        if (mHeaderListArray == null) {
            this.mHeaderListArray = new ArrayList<View>();
        } else {
            this.mHeaderListArray = mHeaderListArray;
        }
        if (mFooterListArray == null) {
            this.mFooterListArray = new ArrayList<View>();
        } else {
            this.mFooterListArray = mFooterListArray;
        }
        this.mAdapter = mAdapter;
    }

    @Override
    public int getItemViewType(int position) {

        // Header (negative positions will throw an IndexOutOfBoundsException)
        int numHeaders = getHeadersCount();
        int numFooters=getFootersCount();
        if (position < numHeaders) {
            this.position=position;
            return RecyclerView.INVALID_TYPE;
        }

        // Adapter
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                this.position=adjPosition;
                return mAdapter.getItemViewType(adjPosition);
            }
        }
        this.position=position-numHeaders-adapterCount;
        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
        return RecyclerView.INVALID_TYPE - 1;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == RecyclerView.INVALID_TYPE) {
            return new HeaderViewHolder(mHeaderListArray.get(position));
        } else if (viewType == (RecyclerView.INVALID_TYPE - 1)) {
            return new HeaderViewHolder(mFooterListArray.get(position));
        }
        return mAdapter.onCreateViewHolder(parent, viewType);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        int numHeaders = getHeadersCount();
        if (position < numHeaders) {
            return;
        }
        //adapter列表部分
        final int adjPosition = position - numHeaders;
        int adapterCount = 0;
        if (mAdapter != null) {
            adapterCount = mAdapter.getItemCount();
            if (adjPosition < adapterCount) {
                mAdapter.onBindViewHolder(holder, adjPosition);
                return;
            }
        }

        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
    }

    @Override
    public int getItemCount() {
        if (mAdapter != null) {
            return getFootersCount() + getHeadersCount() + mAdapter.getItemCount();
        } else {
            return getFootersCount() + getHeadersCount();
        }
    }

    public int getHeadersCount() {
        return mHeaderListArray.size();
    }

    public int getFootersCount() {
        return mFooterListArray.size();
    }

    public static class HeaderViewHolder extends RecyclerView.ViewHolder {

        public HeaderViewHolder(View itemView) {
            super(itemView);
        }
    }
}
  • 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

关于添加头布局部分代码我想大家都会把。和listView都一样:我就补贴代码了:运行效果如图一
代码下载地址guthub:https://github.com/luhenchang/Lsn5_MaterialDesign_WrapRecylerView.git

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/161943
推荐阅读
相关标签
  

闽ICP备14008679号