RecyclerView添加header与footer
前言
这次主要关于RecyclerView
添加header
和footer
的实现方法,我们都知道,在使用ListView
的时候我们能自由的给自己的ListView
添加头部与尾部。使用addHeaderView()
与addFooterView()
方法实现。只要自己自定义布局文件,添加进去即可。但当我们使用RecyclerView
的时候就会发现这两个方便的方法没有了。那么如果此时我们要实现这些功能时要怎么办呢?不急,下面我们先来熟悉下ListView
的添加header
与footer
的实现原理。
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
源码中,发现他就相对于一个我们自定义的Adapter
对header
与footer
的封装。
代码都比较简单,主要是思想,其中主要的是有
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
小于header
的count
时,此时返回头部View
,当position
减去headeer
的count
时依然小于正常的adapter
的count
,此时返回正常的View
,如果还有,自然剩下的就是footer
了。
所以根据ListView
的添加头部与尾部的实现原理,我们可以模仿实现RecyclerView
的相同的功能。
EnhanceRecyclerView
那么我们根据ListView
来实现一个能够添加header
与footer
的RecyclerView
,我这里定义为EnhanceRecyclerView
FixedViewInfo
在上面我们看到ListView
中使用到了FixedViewInfo
,就是一个自定义的类,用来存储数据,我们做适当的修改如下,View
还是不变,依然是添加的视图,将其余的删掉,换成viewType
。
public class FixedViewInfo {public View view;public int viewType;}
addHeaderView与addFooterView
做相应的修改,其中BASE_HEADER_VIEW_TYPE
与BASE_FOOTER_VIEW_TYPE
是一个final
的int
数据
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
我们也可以根据ListView
的HeaderViewListAdapter
来模仿实现一个封装头部与尾部的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
来实现不同的布局文件.不是header
与footer
布局时则直接回调原始adapter
的onCreateViewHolder()
方法。实现正常的布局。
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
属于正常的位置范围之内时,则回调原始的adapter
的onBindViewHolder()
方法,实现数据的绑定;对于其它的情况,无需做处理。
主要的模仿变动就是这些,然后自己在根据情况进行适当的修改,最后在布局文件中使用自定义的
EnhanceRecyclerView
替代RecyclerView
调整
我们都知道在使用RecyclerView
的时候要设置LayoutManager
,如果按照上面的实现,当LayoutManager
设置为LinearLayout
时,自然没上面问题,头部与尾部能正常添加;但当我们的LayoutManager
设置为GridLayoutManager
与StaggeredGridLayoutManager
时,我们会发现头部与尾部的添加出现异常,就是他们不能独自占一行,所以这里我们要做相应的调整,使他们在添加头部与尾部的时候独自占一行。
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;}}
可以看出当LayoutManager
为GridLayoutManager
时,我们通过manager
来设置setSpanSizeLookup()
在getSpanSize()
方法中根据具体判断来调用 manager.getSpanCount()
来实现头部与尾部的占一行的效果;对于StaggeredGridLayoutManager
因为其没有setSpanSizeLookup()
方法,所以我们先做标记,在ViewHolder
中为每一个需要的ItemView
来设置params
,setFullSpa(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);}
最后再 在EnhanceRecyclerView
的setAdapter()
方法中根据标记来判断是否调用adjustSpanSize()
方法来进行调整header
与footer
.
if (isShouldSpan) {((WrapperRecyclerViewAdapter) mAdapter).adjustSpanSize(this);}
其实就是前面setAdapter()
中所注释的代码。
这样整个调整部分就基本完成。header
与footer
的添加也就基本完成了。
总结
通过RecyclerView
的头部与尾部的添加,我们应该能学习如何运用原有的资源来实现我们所需要的功能。虽然方法不一定是最好的,因为使用这种方法实现也存在一些问题,例如不能在初始化(onCreate)中同时添加header
与footer
,否则会出现布局混乱的现象,现在还不知道为何有这种特例,希望知道的可以告知解决下,但这种模仿的思想还是很值得推荐的。
个人blog地址:https://idisfkj.github.io/arc...
关注
RecyclerView添加header与footer相关推荐
- 第七章:Paging添加header和footer
paging使用:https://huangxiaoguo.blog.csdn.net/article/details/106567399 效果 封装可添加Header和Footer的BaseAdap ...
- RecyclerView添加Header的正确方式
看了一下博客目录,已经有好几篇博客是关于RecyclerView的,不过对于这么一款强大的控件,我还是要再写一篇博客来学习一下,这篇博客的主题是<为RecyclerView添加header> ...
- Android RecyclerView添加Header头部
Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...
- android recyclerview添加头部,Android RecyclerView添加Header头部
Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...
- StroyBoard中UICollectionView中添加Header和footer
到Storyboard中,选择collection view controller中的"Collection View".在Attributes inspector中,选择&quo ...
- Android框架之路——Banner实现轮播图(RecyclerView添加Header)
一.简介 Banner能实现循环播放多个广告图片和手动滑动循环等功能.因为原生ViewPager并不支持循环翻页, 要实现循环还得需要自己去动手.Banner框架可以进行不同样式.不同动画设置, 以及 ...
- 实现带header和footer功能的RecyclerView——完善篇
在上一篇文章中我们实现了实现带header和footer功能的RecyclerView,见 实现带header和footer功能的RecyclerView 但是由于加入了header,item的po ...
- 实现带header和footer功能的RecyclerView
这个项目很简单,其实一年前就开发完成了,但是一直没闲下来去整理. RecyclerView是Android 5.0版本引入的一个新的组件,目的是在一些场景中取代之前ListView和GridView, ...
- 为RecyclerView添加下拉刷新(PullToRefresh)功能
在之前的文章中,我们实现了带有header和footer功能的WrapRecyclerView. 实现带header和footer功能的RecyclerView 实现带header和footer功能的 ...
最新文章
- 态势“知”多少,点开就知道
- 谈一谈我对AI项目落地的看法
- 肖邦夜曲21_原装进口 | 肖邦夜曲全集 鲁宾斯坦 钢琴经典 2CD
- activiti如何最后一次提交事务_MySQL如何找出未提交事务的SQL浅析
- arcgis栅格邻域统计_ArcGIS 从基础到实战书正式出版,易智瑞技术总裁沙志友沙总推荐并亲自写序...
- 60级怀旧服最新服务器人口排名,魔兽世界怀旧服10月最新人口普查详情 各大服务器阵营详细比例...
- 在CentOS 7上安装Node.js的4种方法(yum安装和源码安装)
- 各种VC9 VC11版本的Apache
- php抽奖简单源码,php转盘抽奖 完整例子源码(含模拟数据)
- 从随机生成九宫格至随机生成数独游戏再至用回溯法实现数独的解
- 制作动画的素材和资源
- ijkplayer设置rtmp秒开
- 二进制安装PLG日志服务
- python寻峰_python – 查找峰的全宽半个最大值
- sql 查询及格率优秀率
- order by 、sort by、distribute by、group by、cluster by的区别
- 计算机科学计算矩阵答案,计算机科学计算答案.doc
- 如何读计算机专业研究生
- python中集合运算_Python—集合的操作、文件的操作
- Linux系统管理--(3)定时、延时任务与存储的基本管理
热门文章
- arm linux考勤,定稿毕业论文_基于ARM与Linux的员工刷卡考勤系统喜欢就下吧(范文1)...
- java读取属性文件的方法_java读取属性文件的方法
- php 跳转网页 变量,php变量与JS变量实现不通过跳转直接交互的方法
- flash 多张相片同时移动_Flash时代时代即将终止?早就预料到了
- css a标签去掉下划线_这30个CSS选择器,你必须熟记(上)
- 格式引用_论文中如何快速给出规范的文献引用格式?
- 百练OJ:2799:浮点数格式
- 【考前必知】软考考前注意事项
- 笔记-项目整体管理-开工会议-kick-off-meeting
- 《系统集成项目管理工程师》必背100个知识点-81风险管理