版权声明:本文为博主原创文章,未经博主允许不得转载。
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