RecyclerView 添加头部和尾部布局

RecyclerView 添加头部和尾部布局

标签: 头布局尾布局
4190人阅读 评论(6) 收藏 举报
本文章已收录于:

RecyclerView 出来有很长一段时间了,相信大家对它已经很熟悉了,使用过它的朋友可能都会发现一点,就是 RecyclerView 不能添加 headerView 和 footView,这就让我们有点蛋疼了,也许你会说,没事啊,我们可以重写getItemViewType(int position)这个方法,让他实现多个布局,具体实现如下:

<code class="hljs java has-numbering">    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getItemViewType</span>(<span class="hljs-keyword">int</span> position) {
        <span class="hljs-keyword">if</span> (position == <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">return</span> HEAD_VIEW;
        }<span class="hljs-keyword">else</span>{
            <span class="hljs-keyword">return</span> BODY_VIEW ;
        }
    }
</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li></ul><div class="save_code tracking-ad" data-mod="popu_249" style="display: none;"><a target=_blank href="javascript:;"><img src="http://static.blog.youkuaiyun.com/images/save_snippets.png" /></a></div>

下面的操作就不多讲了,这种方法相信大家在使用 ListView 的时候就非常熟练; 那既然可以通过以上方法实现,为什么还要写一个可以添加 HeaderView 和 FootView 的 RecyclerView ?其实这个问题不用回答,因为 ListView 也可以通过以上方法实现,但它还是有 addHeaderView和 addFootView 的方法,不过话说回来,通过 addHeaderView 实现的添加头部布局确实有不可代替的功能,比如,ListView 头部图片下拉放大动画;在调用 adapter.notifyDataSetChanged()头部数据是不会重新刷新的等等。

好了,接下来才是本文的主题内容,如何实现 RecyclerView 添加 HeaderView 和 FootView?

无从下手?没关系!ListView 不是已经帮我们实现了这样的功能吗,我们可以先看看 ListView 是如何实现的,

<code class="hljs java has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addHeaderView</span>(View v, Object data, <span class="hljs-keyword">boolean</span> isSelectable) {
        <span class="hljs-keyword">final</span> FixedViewInfo info = <span class="hljs-keyword">new</span> FixedViewInfo();
        info.view = v;
        info.data = data;
        info.isSelectable = isSelectable;
        mHeaderViewInfos.add(info);
        mAreAllItemsSelectable &= isSelectable;

        <span class="hljs-comment">// Wrap the adapter if it wasn't already wrapped.</span>
        <span class="hljs-keyword">if</span> (mAdapter != <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">if</span> (!(mAdapter <span class="hljs-keyword">instanceof</span> HeaderViewListAdapter)) {
                mAdapter = <span class="hljs-keyword">new</span> HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
            }

            <span class="hljs-comment">// In the case of re-adding a header view, or adding one later on,</span>
            <span class="hljs-comment">// we need to notify the observer.</span>
            <span class="hljs-keyword">if</span> (mDataSetObserver != <span class="hljs-keyword">null</span>) {
                mDataSetObserver.onChanged();
            }
        }
    }
</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li></ul><div class="save_code tracking-ad" data-mod="popu_249" style="display: none;"><a target=_blank href="javascript:;"><img src="http://static.blog.youkuaiyun.com/images/save_snippets.png" /></a></div>

以上方法核心代码就是

<code class="hljs fix has-numbering"><span class="hljs-attribute">mAdapter </span>=<span class="hljs-string"> new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);</span></code><ul class="pre-numbering"><li>1</li></ul><div class="save_code tracking-ad" data-mod="popu_249" style="display: none;"><a target=_blank href="javascript:;"><img src="http://static.blog.youkuaiyun.com/images/save_snippets_01.png" /></a></div>

其他可以忽略不看;这里其实用到了一种设计模式的概念,好像是叫什么装饰模式吧,意思就是把原有的 Adapter 通过HeaderViewListAdapter这个包装类重新包装一次,把改增的地方增一增,该减的地方减一点;所以我们要关注的是HeaderViewListAdapter类是如何实现的,然后再依葫芦画瓢画一个包装 RecyclerView.Adapter的包装类!

解密HeaderViewListAdapter
这个包装类代码本来就不长,相信大家都能够理解的,这里就挑点重点讲一下
1、对Adapter 的 count 进行了重新计算,这个不用多解释吧!

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getCount</span>() {
        <span class="hljs-keyword">if</span> (mAdapter != <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> getFootersCount() + getHeadersCount() + mAdapter.getCount();
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">return</span> getFootersCount() + getHeadersCount();
        }
    }
</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li></ul><div class="save_code tracking-ad" data-mod="popu_249" style="display: none;"><a target=_blank href="javascript:;"><img src="http://static.blog.youkuaiyun.com/images/save_snippets.png" /></a></div>

2、就是getView方法,这个方法是重点,如果当前 position 的位置比 HeaderView 的数量小,那么返回的就是 HeaderView 的 对应的 View,否则再判断 原 Adapter 的 count 与当前 position 的差值来比较,是调用原 Adapter 的 getView 方法,还是获取 footView 的 view;说白了这个方法的目的就是添加了头部和尾部 View。

<code class="hljs cs has-numbering">    <span class="hljs-keyword">public</span> View <span class="hljs-title">getView</span>(<span class="hljs-keyword">int</span> position, View convertView, ViewGroup parent) {
        <span class="hljs-comment">// Header (negative positions will throw an IndexOutOfBoundsException)</span>
        <span class="hljs-keyword">int</span> numHeaders = getHeadersCount();
        <span class="hljs-keyword">if</span> (position < numHeaders) {
            <span class="hljs-keyword">return</span> mHeaderViewInfos.<span class="hljs-keyword">get</span>(position).view;
        }

        <span class="hljs-comment">// Adapter</span>
        final <span class="hljs-keyword">int</span> adjPosition = position - numHeaders;
        <span class="hljs-keyword">int</span> adapterCount = <span class="hljs-number">0</span>;
        <span class="hljs-keyword">if</span> (mAdapter != <span class="hljs-keyword">null</span>) {
            adapterCount = mAdapter.getCount();
            <span class="hljs-keyword">if</span> (adjPosition < adapterCount) {
                <span class="hljs-keyword">return</span> mAdapter.getView(adjPosition, convertView, parent);
            }
        }

        <span class="hljs-comment">// Footer (off-limits positions will throw an IndexOutOfBoundsException)</span>
        <span class="hljs-keyword">return</span> mFooterViewInfos.<span class="hljs-keyword">get</span>(adjPosition - adapterCount).view;
    }</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li></ul><div class="save_code tracking-ad" data-mod="popu_249" style="display: none;"><a target=_blank href="javascript:;"><img src="http://static.blog.youkuaiyun.com/images/save_snippets.png" /></a></div>

对于这个包装类的其他方法只要注意一下就 没问题了,那么接下来,就是我们模仿的时刻了!
代码非常简单,这里就直接上了

1、包装类 RecyclerWrapAdapter

package moon.myapplication;

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

import java.util.ArrayList;

/**
* Created by moon.zhong on 2015/7/20.
* time : 14:10
*/
public class RecyclerWrapAdapter extends RecyclerView.Adapter implements WrapperAdapter {

private RecyclerView.Adapter mAdapter;

private ArrayList<View> mHeaderViews;

private ArrayList<View> mFootViews;

static final ArrayList<View> EMPTY_INFO_LIST =
        new ArrayList<View>();

private int mCurrentPosition;

public RecyclerWrapAdapter(ArrayList<View> mHeaderViews, ArrayList<View> mFootViews, RecyclerView.Adapter mAdapter) {
    this.mAdapter = mAdapter;
    if (mHeaderViews == null) {
        this.mHeaderViews = EMPTY_INFO_LIST;
    } else {
        this.mHeaderViews = mHeaderViews;
    }
    if (mHeaderViews == null) {
        this.mFootViews = EMPTY_INFO_LIST;
    } else {
        this.mFootViews = mFootViews;
    }
}

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

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

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

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

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

@Override
public int getItemViewType(int position) {
    mCurrentPosition = position;
    int numHeaders = getHeadersCount();
    if (position < numHeaders) {
        return RecyclerView.INVALID_TYPE;
    }
    int adjPosition = position - numHeaders;
    int adapterCount = 0;
    if (mAdapter != null) {
        adapterCount = mAdapter.getItemCount();
        if (adjPosition < adapterCount) {
            return mAdapter.getItemViewType(adjPosition);
        }
    }
    return RecyclerView.INVALID_TYPE - 1;
}


@Override
public long getItemId(int position) {
    int numHeaders = getHeadersCount();
    if (mAdapter != null && position >= numHeaders) {
        int adjPosition = position - numHeaders;
        int adapterCount = mAdapter.getItemCount();
        if (adjPosition < adapterCount) {
            return mAdapter.getItemId(adjPosition);
        }
    }
    return -1;
}


@Override
public RecyclerView.Adapter getWrappedAdapter() {
    return mAdapter;
}

private static class HeaderViewHolder extends RecyclerView.ViewHolder {
    public HeaderViewHolder(View itemView) {
        super(itemView);
    }
}

}

2、为 RecyclerView 添加 AddHeaderView 和 addFootView 方法,重写 RecyclerView

<code class="hljs java has-numbering"><span class="hljs-keyword">package</span> moon.myapplication;

<span class="hljs-keyword">import</span> android.content.Context;
<span class="hljs-keyword">import</span> android.support.v7.widget.RecyclerView;
<span class="hljs-keyword">import</span> android.util.AttributeSet;
<span class="hljs-keyword">import</span> android.view.View;

<span class="hljs-keyword">import</span> java.util.ArrayList;

<span class="hljs-javadoc">/**
 * Created by moon.zhong on 2015/7/20.
 * time : 15:14
 */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WrapRecyclerView</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">RecyclerView</span> {</span>

    <span class="hljs-keyword">private</span> ArrayList<View> mHeaderViews = <span class="hljs-keyword">new</span> ArrayList<>() ;

    <span class="hljs-keyword">private</span> ArrayList<View> mFootViews = <span class="hljs-keyword">new</span> ArrayList<>() ;

    <span class="hljs-keyword">private</span> Adapter mAdapter ;

    <span class="hljs-keyword">public</span> <span class="hljs-title">WrapRecyclerView</span>(Context context) {
        <span class="hljs-keyword">super</span>(context);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-title">WrapRecyclerView</span>(Context context, AttributeSet attrs) {
        <span class="hljs-keyword">super</span>(context, attrs);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-title">WrapRecyclerView</span>(Context context, AttributeSet attrs, <span class="hljs-keyword">int</span> defStyle) {
        <span class="hljs-keyword">super</span>(context, attrs, defStyle);
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addHeaderView</span>(View view){
        mHeaderViews.clear();
        mHeaderViews.add(view);
        <span class="hljs-keyword">if</span> (mAdapter != <span class="hljs-keyword">null</span>){
            <span class="hljs-keyword">if</span> (!(mAdapter <span class="hljs-keyword">instanceof</span> RecyclerWrapAdapter)){
                mAdapter = <span class="hljs-keyword">new</span> RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter) ;
<span class="hljs-comment">//                mAdapter.notifyDataSetChanged();</span>
            }
        }
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addFootView</span>(View view){
        mFootViews.clear();
        mFootViews.add(view);
        <span class="hljs-keyword">if</span> (mAdapter != <span class="hljs-keyword">null</span>){
            <span class="hljs-keyword">if</span> (!(mAdapter <span class="hljs-keyword">instanceof</span> RecyclerWrapAdapter)){
                mAdapter = <span class="hljs-keyword">new</span> RecyclerWrapAdapter(mHeaderViews,mFootViews,mAdapter) ;
<span class="hljs-comment">//                mAdapter.notifyDataSetChanged();</span>
            }
        }
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAdapter</span>(Adapter adapter) {

        <span class="hljs-keyword">if</span> (mHeaderViews.isEmpty()&&mFootViews.isEmpty()){
            <span class="hljs-keyword">super</span>.setAdapter(adapter);
        }<span class="hljs-keyword">else</span> {
            adapter = <span class="hljs-keyword">new</span> RecyclerWrapAdapter(mHeaderViews,mFootViews,adapter) ;
            <span class="hljs-keyword">super</span>.setAdapter(adapter);
        }
        mAdapter = adapter ;
    }

}</code><ul class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li><li>55</li><li>56</li><li>57</li><li>58</li><li>59</li><li>60</li><li>61</li><li>62</li><li>63</li><li>64</li><li>65</li><li>66</li><li>67</li><li>68</li></ul><div class="save_code tracking-ad" data-mod="popu_249" style="display: none;"><a target=_blank href="javascript:;"><img src="http://static.blog.youkuaiyun.com/images/save_snippets.png" /></a></div>

到这里就实现了 RecyclerView 添加头部和尾部布局 ,用法很简单,其他步骤都不变,只需把 RecyclerView 换成WrapRecyclerView即可。

看看效果图
这里写图片描述

这篇文章没有什么创新的技术点,但却给广大程序员一个很好的思路:那就是模仿,最好是能自己动手,也许你看一遍就能理解,但是真正动手实践的时候你会发现很多问题!

源码 Download

6
0
 
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值