前言

这次主要关于RecyclerView添加headerfooter的实现方法,我们都知道,在使用ListView的时候我们能自由的给自己的ListView添加头部与尾部。使用addHeaderView()addFooterView()方法实现。只要自己自定义布局文件,添加进去即可。但当我们使用RecyclerView的时候就会发现这两个方便的方法没有了。那么如果此时我们要实现这些功能时要怎么办呢?不急,下面我们先来熟悉下ListView的添加headerfooter的实现原理。

ListView源码

既然ListView实现了添加头部与尾部的原理,所以我们可以先进入ListView的源码,查看其中的addHeaderView()方法与addFooterView()方法。

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)) {mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);}// 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();}}}

这里省略addFooterView()源码,其实跟addHeaderView()基本一致

通过addHeaderView()的源码我们可以发现,它是使用一个FixedViewInfo类来存储数据。

public class FixedViewInfo {/** The view to add to the list */public View view;/** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */public Object data;/** <code>true</code> if the fixed view should be selectable in the list */public boolean isSelectable;}

其中最主要的代码就是

 if (!(mAdapter instanceof HeaderViewListAdapter)) {mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);}

我们会发现它new了一个HeaderViewListAdapter类,那么我们再进入HeaderViewListAdapter源码中,发现他就相对于一个我们自定义的Adapterheaderfooter的封装。

代码都比较简单,主要是思想,其中主要的是有

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

根据position来设置ViewType的值

 public int getItemViewType(int position) {int numHeaders = getHeadersCount();if (mAdapter != null && position >= numHeaders) {int adjPosition = position - numHeaders;int adapterCount = mAdapter.getCount();if (adjPosition < adapterCount) {return mAdapter.getItemViewType(adjPosition);}}return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;}

再看getView()方法

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;}// Adapterfinal 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;}

getItemViewType类似,当position小于headercount时,此时返回头部View,当position减去headeercount时依然小于正常的adaptercount,此时返回正常的View,如果还有,自然剩下的就是footer了。

所以根据ListView的添加头部与尾部的实现原理,我们可以模仿实现RecyclerView的相同的功能。

EnhanceRecyclerView

那么我们根据ListView来实现一个能够添加headerfooterRecyclerView,我这里定义为EnhanceRecyclerView

FixedViewInfo

在上面我们看到ListView中使用到了FixedViewInfo,就是一个自定义的类,用来存储数据,我们做适当的修改如下,View还是不变,依然是添加的视图,将其余的删掉,换成viewType

public class FixedViewInfo {public View view;public int viewType;}

addHeaderView与addFooterView

做相应的修改,其中BASE_HEADER_VIEW_TYPEBASE_FOOTER_VIEW_TYPE是一个finalint数据

public void addHeaderView(View view) {FixedViewInfo info = new FixedViewInfo();info.view = view;info.viewType = BASE_HEADER_VIEW_TYPE + mHeaderViewInfos.size();mHeaderViewInfos.add(info);if (mAdapter != null) {mAdapter.notifyDataSetChanged();}}
    public void addFooterView(View view) {FixedViewInfo info = new FixedViewInfo();info.view = view;info.viewType = BASE_FOOTER_VIEW_TYPE + mFooterViewInfos.size();mFooterViewInfos.add(info);if (mAdapter != null) {mAdapter.notifyDataSetChanged();}}

setAdapter

下面是setAdapter(),使用后面封装的WrapperRecyclerViewAdapter

    @Overridepublic void setAdapter(Adapter adapter) {if (!(adapter instanceof WrapperRecyclerViewAdapter))mAdapter = new WrapperRecyclerViewAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);super.setAdapter(mAdapter);//        if (isShouldSpan) {
//            ((WrapperRecyclerViewAdapter) mAdapter).adjustSpanSize(this);
//        }}

WrapperRecyclerViewAdapter

我们也可以根据ListViewHeaderViewListAdapter来模仿实现一个封装头部与尾部的adapter。我这里定义为WrapperRecyclerViewAdapter继承RecyclerView.Adapter根据情况适当修改。

getItemCount

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

getItemViewType

@Overridepublic int getItemViewType(int position) {int numHeaders = getHeadersCount();if (position < numHeaders) {return mHeaderViewInfos.get(position).viewType;}int adjPosition = position - numHeaders;int adapterPosition = 0;if (mAdapter != null) {adapterPosition = mAdapter.getItemCount();if (adjPosition < adapterPosition) {return mAdapter.getItemViewType(adjPosition);}}return mFooterViewInfos.get(position - adapterPosition - getHeadersCount()).viewType;}

这里逻辑与ListView的基本一致。

onCreateViewHolder

这里需将HeaderViewListAdapter中的getView()方法分成两部分来实现。

    @Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if (viewType >= EnhanceRecyclerView.BASE_HEADER_VIEW_TYPE && viewType < EnhanceRecyclerView.BASE_HEADER_VIEW_TYPE + getHeadersCount()) {View view = mHeaderViewInfos.get(viewType - EnhanceRecyclerView.BASE_HEADER_VIEW_TYPE).view;return viewHolder(view);} else if (viewType >= EnhanceRecyclerView.BASE_FOOTER_VIEW_TYPE && viewType < EnhanceRecyclerView.BASE_FOOTER_VIEW_TYPE + getFootersCount()) {View view = mFooterViewInfos.get(viewType - EnhanceRecyclerView.BASE_FOOTER_VIEW_TYPE).view;return viewHolder(view);}return mAdapter.onCreateViewHolder(parent, viewType);}

根据不同的viewType来实现不同的布局文件.不是headerfooter布局时则直接回调原始adapteronCreateViewHolder()方法。实现正常的布局。

onBindViewHolder

    @Overridepublic 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;}}}

根据position的位置来选择,当position属于正常的位置范围之内时,则回调原始的adapteronBindViewHolder()方法,实现数据的绑定;对于其它的情况,无需做处理。

主要的模仿变动就是这些,然后自己在根据情况进行适当的修改,最后在布局文件中使用自定义的EnhanceRecyclerView替代RecyclerView

调整

我们都知道在使用RecyclerView的时候要设置LayoutManager,如果按照上面的实现,当LayoutManager设置为LinearLayout时,自然没上面问题,头部与尾部能正常添加;但当我们的LayoutManager设置为GridLayoutManagerStaggeredGridLayoutManager时,我们会发现头部与尾部的添加出现异常,就是他们不能独自占一行,所以这里我们要做相应的调整,使他们在添加头部与尾部的时候独自占一行。

adjustSpanSize

在上面我们自定义的WrapperRecyclerViewAdapter中添加adjustSpanSize()方法,用来实现调整。

public void adjustSpanSize(RecyclerView recyclerView) {if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {final GridLayoutManager manager = (GridLayoutManager) recyclerView.getLayoutManager();manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {int numHeaders = getHeadersCount();int adjPosition = position - numHeaders;if (position < numHeaders || adjPosition >= mAdapter.getItemCount())return manager.getSpanCount();return 1;}});}if (recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {isStaggered = true;}}

可以看出当LayoutManagerGridLayoutManager时,我们通过manager来设置setSpanSizeLookup()getSpanSize()方法中根据具体判断来调用 manager.getSpanCount()来实现头部与尾部的占一行的效果;对于StaggeredGridLayoutManager因为其没有setSpanSizeLookup()方法,所以我们先做标记,在ViewHolder中为每一个需要的ItemView来设置paramssetFullSpa(true)方法来填充一行。

    private RecyclerView.ViewHolder viewHolder(View itemView) {if (isStaggered) {StaggeredGridLayoutManager.LayoutParams params = new StaggeredGridLayoutManager.LayoutParams(StaggeredGridLayoutManager.LayoutParams.MATCH_PARENT,StaggeredGridLayoutManager.LayoutParams.WRAP_CONTENT);params.setFullSpan(true);itemView.setLayoutParams(params);}return new RecyclerView.ViewHolder(itemView) {};}

setLayoutManager

EnhanceRecyclerView中重写setLayoutManager()方法,添加标记。

@Overridepublic void setLayoutManager(LayoutManager layout) {if (layout instanceof GridLayoutManager || layout instanceof StaggeredGridLayoutManager)isShouldSpan = true;super.setLayoutManager(layout);}

最后再 在EnhanceRecyclerViewsetAdapter()方法中根据标记来判断是否调用adjustSpanSize()方法来进行调整headerfooter.

if (isShouldSpan) {((WrapperRecyclerViewAdapter) mAdapter).adjustSpanSize(this);}

其实就是前面setAdapter()中所注释的代码。

这样整个调整部分就基本完成。headerfooter的添加也就基本完成了。

总结

通过RecyclerView的头部与尾部的添加,我们应该能学习如何运用原有的资源来实现我们所需要的功能。虽然方法不一定是最好的,因为使用这种方法实现也存在一些问题,例如不能在初始化(onCreate)中同时添加headerfooter,否则会出现布局混乱的现象,现在还不知道为何有这种特例,希望知道的可以告知解决下,但这种模仿的思想还是很值得推荐的。

个人blog地址:https://idisfkj.github.io/arc...

关注

RecyclerView添加header与footer相关推荐

  1. 第七章:Paging添加header和footer

    paging使用:https://huangxiaoguo.blog.csdn.net/article/details/106567399 效果 封装可添加Header和Footer的BaseAdap ...

  2. RecyclerView添加Header的正确方式

    看了一下博客目录,已经有好几篇博客是关于RecyclerView的,不过对于这么一款强大的控件,我还是要再写一篇博客来学习一下,这篇博客的主题是<为RecyclerView添加header> ...

  3. Android RecyclerView添加Header头部

     Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...

  4. android recyclerview添加头部,Android RecyclerView添加Header头部

     Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...

  5. StroyBoard中UICollectionView中添加Header和footer

    到Storyboard中,选择collection view controller中的"Collection View".在Attributes inspector中,选择&quo ...

  6. Android框架之路——Banner实现轮播图(RecyclerView添加Header)

    一.简介 Banner能实现循环播放多个广告图片和手动滑动循环等功能.因为原生ViewPager并不支持循环翻页, 要实现循环还得需要自己去动手.Banner框架可以进行不同样式.不同动画设置, 以及 ...

  7. 实现带header和footer功能的RecyclerView——完善篇

    在上一篇文章中我们实现了实现带header和footer功能的RecyclerView,见  实现带header和footer功能的RecyclerView 但是由于加入了header,item的po ...

  8. 实现带header和footer功能的RecyclerView

    这个项目很简单,其实一年前就开发完成了,但是一直没闲下来去整理. RecyclerView是Android 5.0版本引入的一个新的组件,目的是在一些场景中取代之前ListView和GridView, ...

  9. 为RecyclerView添加下拉刷新(PullToRefresh)功能

    在之前的文章中,我们实现了带有header和footer功能的WrapRecyclerView. 实现带header和footer功能的RecyclerView 实现带header和footer功能的 ...

最新文章

  1. 态势“知”多少,点开就知道
  2. 谈一谈我对AI项目落地的看法
  3. 肖邦夜曲21_原装进口 | 肖邦夜曲全集 鲁宾斯坦 钢琴经典 2CD
  4. activiti如何最后一次提交事务_MySQL如何找出未提交事务的SQL浅析
  5. arcgis栅格邻域统计_ArcGIS 从基础到实战书正式出版,易智瑞技术总裁沙志友沙总推荐并亲自写序...
  6. 60级怀旧服最新服务器人口排名,魔兽世界怀旧服10月最新人口普查详情 各大服务器阵营详细比例...
  7. 在CentOS 7上安装Node.js的4种方法(yum安装和源码安装)
  8. 各种VC9 VC11版本的Apache
  9. php抽奖简单源码,php转盘抽奖 完整例子源码(含模拟数据)
  10. 从随机生成九宫格至随机生成数独游戏再至用回溯法实现数独的解
  11. 制作动画的素材和资源
  12. ijkplayer设置rtmp秒开
  13. 二进制安装PLG日志服务
  14. python寻峰_python – 查找峰的全宽半个最大值
  15. sql 查询及格率优秀率
  16. order by 、sort by、distribute by、group by、cluster by的区别
  17. 计算机科学计算矩阵答案,计算机科学计算答案.doc
  18. 如何读计算机专业研究生
  19. python中集合运算_Python—集合的操作、文件的操作
  20. Linux系统管理--(3)定时、延时任务与存储的基本管理

热门文章

  1. arm linux考勤,定稿毕业论文_基于ARM与Linux的员工刷卡考勤系统喜欢就下吧(范文1)...
  2. java读取属性文件的方法_java读取属性文件的方法
  3. php 跳转网页 变量,php变量与JS变量实现不通过跳转直接交互的方法
  4. flash 多张相片同时移动_Flash时代时代即将终止?早就预料到了
  5. css a标签去掉下划线_这30个CSS选择器,你必须熟记(上)
  6. 格式引用_论文中如何快速给出规范的文献引用格式?
  7. 百练OJ:2799:浮点数格式
  8. 【考前必知】软考考前注意事项
  9. 笔记-项目整体管理-开工会议-kick-off-meeting
  10. 《系统集成项目管理工程师》必背100个知识点-81风险管理