ExtraViewWrapperAdapter--添加额外头部尾部功能的装饰adapter
来源:互联网 发布:betterzip 4 for mac 编辑:程序博客网 时间:2024/05/22 03:10
目录
- 目录
- 概述
- 关于头部和尾部
- 分离原始数据及装饰数据
- headerView与footerView的创建与显示
- 使用唯一的标签
- 关于headerView与FooterView位置的计算
- 头部尾部的判断方式
- 与HeaderRecycleAdapter的接口相关
- 其它
- 使用方式
- GitHub地址
- 示例图片
概述
对于ListView
有自带的方法添加headerView及footerView,但是RecycleView
仅仅只是维护缓存的View,本身并不处理内容显示,都交给了RecycleView.Adapter
处理,所以如果想要让RecycleView
也可以添加headerView和footerView的话,只有两种方式
- 是重写
RecycleView
- 是通过Adapter的方式去解决这个问题,将headerView及footerView转换成内部数据的形式显示出来.
针对这个问题,这里使用第二种方式,扩展了Adapter,通过装饰者模式,只需要将自己的Adatper包括进外层的Adapter中,即可直接添加headerView及footerView,同时不会对原有的数据造成任何影响.
这里需要注意的是,此处的ExtroViewWrapperAdaper
包装类是通用的,但是更针对于HeaderRecycleAdatper
,包括实现了HeaderRecycleAdapter
相关的一些接口用于扩展.
对于普通的Adapter,也是可以直接使用.
关于头部和尾部
由于通过Adapter来完成头部与尾部的功能添加(这样做的好处是在任何的RecycleView
上都是可以使用的),所以实现方式上跟普通的多类型item的Adapter的实现方式是一致的,这里不再强调.主要说明如何进行装饰.
分离原始数据及装饰数据
对于Adapter,由于每个Adapter完成的功能都不同,所以我们只能不能知道Adapter是具体如何完成的,那么我们需要尽量将原始的数据与装饰的数据(头部/尾部)分离开来.ExtraViewWrapperAdapter
主要就是要处理这个.
首先是保存原有Adapter的引用.对于头部和尾部一般是只添加一个,但这里考虑到可能会添加多个,所以是允许添加多个的.
对于添加多个的情况,每一个headerView都可能不同,这时就需要提供每一个headerView的一个viewTag(标志),这个标志是唯一的,用于分辨不同的headerView的类型以显示出来.
对此使用一个内部类来管理添加的headerView或者footerView.
/** * 头部/尾部添加额外View的缓存处理类 */public static class HeaderFooterViewCache { private List<Map.Entry<Integer, View>> mViewCacheMap; private Map<Integer, Integer> mIndexMap; public HeaderFooterViewCache() { mViewCacheMap = new LinkedList<Map.Entry<Integer, View>>(); mIndexMap = new ArrayMap<Integer, Integer>(); } //返回当前保存的View的个数 public int size(); //根据位置获取对应的标签 public int getViewViewTag(int position); //根据标签获取对应的view public View getView(int viewTag); //检测是否已经存在某个标签 public boolean isContainsView(int viewTag); //添加新view及其唯一标签,当该标签已经存在某个view时,将替换该view,返回被替换的view,或者是null;若view为null,返回null,添加失败 public View addView(int viewTag, View view); //移除某个标签对应的view public boolean removeView(int viewTag); //清除所有的view public void clearAllView(); //获取view的标签列表,标签应该是唯一的 public Set<Integer> getViewTags();}
这里仅给出部分方法签名及其作用说明.具体实现暂不给出.这里缓存时使用两个不同的数据结构,一个是List,一个是Map,原因是List用来保存添加的view的顺序,Map是用于根据标签匹配并保存添加的view.
headerView与footerView的创建与显示
Adapter是自己加载layout并创建view的,但是headerView与footerView是不同的,是直接添加的view并不通过adapter加载与绑定数据.所以在这里使用的ViewHolder
也只是一个临时保存的容器而已.
在onCreateViewHolder()
中创建headerView与footerView的holder
@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new ExtraViewHolder(mHeaderCache.getView(viewType));}
ExtraViewHolder
类的实现仅仅是继承RecycleView.ViewHolder
而已,并没有任何其它的操作.这里只是为了配合Adapter的实现方式.
headerView与footerView的创建完全由外部处理,Adapter并不作任何处理(仅仅是缓存并显示而已),view的获取是通过缓存的HeaderFooterViewCache
类通过唯一的viewTag(标签)进行获取的.
使用唯一的标签
由以上可知,headerView与FooterView是被HeaderFooterViewCache
缓存的,并且使用唯一的viewTag进行标识.
使用Map的原因是标签必须是唯一的,一个标签也只能用于一个view,这样在Adapter加载view时才不会导致有关viewType问题.
以下为Adapter需要实现的一些方法.
//根据位置获取view的类型public int getItemViewType(int position){ //这里仅给出headerView位置的代码,其它位置代码忽略 return mHeaderCache.getViewViewTag(position);}//根据view的类型加载viewpublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){ //这里仅给出headerView的holder创建 return new ExtraViewHolder(mHeaderCache.getView(viewType));}
Map中的唯一的viewTag标签就是用于此处的viewType,由于加载holder是通过viewType来创建的,所以不同的viewTag代表了不同的viewType,也是代表了不同的view.
通过这种方式处理的原因是,headerView与footerView是在创建ExtraViewHolder
时需要使用的,而创建一个holder只会在onCreateViewHolder()
中进行创建.此时参数来源仅有两个:
- parent,父容器
- viewType,item类型
这两个参数显然仅有第二个参数可以使用.由于headerView和FooterView理论上也不会有很多,因此可以通过指定唯一的viewTag匹配对应的view,同时将viewTag传递到onCreateViewHolder()
就可以获取到该tag对应的view了.
对此,在Adapter的getItemViewType()
方法中返回的viewType实际上也就是view所对应的viewTag.这样在一定程度上可以减少很多的计算与匹配工作.
关于headerView与FooterView位置的计算
RecycleView
很重要的一个特点是根据position进行显示item.由于添加了headerView与FooterView,所以原始Adapter中position相对于原来的位置必定会改变.
这里需要计算headerView与FooterView的数量.关于这部分在前面给出的缓存类HeaderFooterViewCache
中已经有相关的方法了,所以可以通过该方法直接获取其对应的数量.
private int getHeaderViewCount() { return mHeaderCache.size();}
这里需要注意,headerView是通过mHeaderCache缓存管理的,footerView是通过mFooterCache缓存管理的,并不是同一个类管理两种view.
得到headerView或者footerView的数量后,就可以很方便地计算了.headerView必定在InnerAdapter的item之前,所以position对应的前面都是headerView.
//headerView的positionint headerPosition=position;//innerAdapter中item的position,除去header部分int innerPosition=position-headerViewCount;//footerView的position,除去header及innerAdapter的item数量int footerPosition=position-headerViewCount-innerAdapter.getItemCount();
ExtraViewWrapperAdapter
提供了获取内部adapter的位置的方法,该方法就是基于以上的方式进行计算得到的.
//这里的wrapAdapterPosition指 ExtraViewWrapperAdapter 对应的positionpublic int getInnerAdapterPosition(int wrapAdapterPosition) { int headerViewCount = getHeaderViewCount(); int innerPosition = wrapAdapterPosition - headerViewCount; if (mInnerAdapter != null && innerPosition < mInnerAdapter.getItemCount()) { return innerPosition; } else { return -1; }}
头部尾部的判断方式
通过以上计算头部尾部的位置,我们是可以得到他们的判断方式的.因为判断头部尾部是给定一个position,判断其是否为头部或者尾部.
//当position在前面的位置且位于headerViewCount的数量范围内,则说明当前位置为头部private boolean isHeaderView(int position) { //计算头部view结束的位置 int headerEndPosition = getHeaderViewCount(); return position >= 0 && position < headerEndPosition;}
尾部的计算方式也是相同的.都是计算当前位置是否在指定的view类型范围内即可.
与HeaderRecycleAdapter
的接口相关
由于ExtraViewWrapperAdapter
是为了兼容HeaderRecycleAdapter
,所以实现了与其相同的一些接口,包括:
- IStickerHeaderDecoration,用于显示固定头部
- ISpanSizeHandler,用于兼容显示GridLayout的布局方式
由于innerAdapter有自己的接口实现,wrapperAdapter并不能代替其实现的功能,所以只能是通过保存对应的接口实现,并在实现这些接口时(与position有关的方法中)屏蔽headerView及footerView的部分,有关innerAdapter的item部分由其对应的接口自行处理.
大致类似以下的处理方式,此处与两个接口有关,需要了解的请查看HeaderRecycleAdapter
及StickHeaderItemDecoration
两个类的分析文章.
//如 IStickerHeaderDecoration 接口中,判断当前position的item是否有固定头部时@Overridepublic boolean hasStickHeader(int position) { //当前位置的item为headerView或者footerView,都不存在固定头部 if (isHeaderView(position) || isFooterView(position)) { return false; } else if (mIStickHeaderDecoration != null) { //若不是,则说明当前位置应该是innerAdapter中的item,计算innerAdapter中对应的位置并回调其相关的接口处理 return mIStickHeaderDecoration.hasStickHeader(getInnerAdapterPosition(position)); } else { return false; }}
这里涉及到了原innerAdapter相关的一些接口问题.由于HeaderRecycleAdapter
是已经实现了相关的接口,所以可以在保存adapter时保存其实现接口的引用.
public void setInnerAdapter(@NonNull RecyclerView.Adapter innerAdapter) { mInnerAdapter = innerAdapter; //当前adapter实现了 IStickerHeaderDecoration接口,则保存其接口引用 if (mInnerAdapter != null && mInnerAdapter instanceof StickHeaderItemDecoration.IStickerHeaderDecoration) { mIStickHeaderDecoration = (StickHeaderItemDecoration.IStickerHeaderDecoration) mInnerAdapter; } //当前adapter实现了 ISpanSizeHandler接口,则保存其接口引用 if (mInnerAdapter != null && mInnerAdapter instanceof HeaderSpanSizeLookup.ISpanSizeHandler) { mISpanSizeLookup = (HeaderSpanSizeLookup.ISpanSizeHandler) mInnerAdapter; }}
通过直接判断adatper是否实现了对应接口直接保存引用,可以更准确地绑定innerAdapter及其相关接口的实现.同时,也存在相关的接口直接设置方法手动绑定当前innerAdapter的实现接口.
其它
ExtraViewWrapperAdapter
大致实现的原理如上,主要是为了增加headerView及footerView的添加功能,同时又能兼容原有innerAdapter的功能(主要指HeaderRecycleAdapter
).
同时提供了添加多个headerView及footerView的功能,并额外添加了refreshView(刷新View)及loadView(加载View)分别在顶部及底部显示,默认为null不存在.
//设置刷新显示的viewmExtraAdapter.setRefreshingHeaderView(yourRefreshView);//切换刷新/加载/header/footer显示的状态ExtraViewWrapAdapter.setRefreshingViewStatus(true,true,rv);
使用方式
使用方式很简单,需要添加headerView或者footerView时,直接进行添加,然后设置原始数据innerAdatper,如果是实现了ISpanSizeHandler
及IStcikerHeaderDecoration
接口的adapter则不需要作额外处理,否则需要考虑一下是否要设置相关的接口实现.
//设置innearAdaptermExtraAdapter = new ExtraViewWrapAdapter(mNormalAdapter);//添加headerViewmExtraAdapter.addHeaderView(R.id.header_view, yourView);//使用extraViewWrapperAdapter代码原有的adapter作为recycleView的数据绑定rv.setAdapter(mExtraAdapter);
GitHub地址
https://github.com/CrazyTaro/RecycleViewAdapter
与之前的RecycleView相关的都置于同一个项目中
示例图片
- ExtraViewWrapperAdapter--添加额外头部尾部功能的装饰adapter
- recyclerview 添加头部和尾部的优雅
- RecyclerView添加头部尾部
- ListView 添加头部和尾部
- RecyclerView添加头部和尾部
- ListView 添加 头部 和 尾部
- RecyclerView添加头部和尾部
- RecycleView添加头部和尾部
- SWIFT写的web view添加头部与尾部视图
- HeaderAndFooterRecyclerView 带emptyview、头部header和尾部footer的封装 通用Adapter ViewHolder
- RecyclerView 添加头部和尾部布局
- 图文 dede 添加 头部 尾部 js调用
- RecyclerView LayoutManager分析 添加头部尾部
- Android RecyclerView添加头部和尾部
- RecycleView添加头部尾部原理笔记
- RecyclerView 添加头部和尾部布局
- RecyclerView 添加头部和尾部布局
- 为RecyclerView添加头部和尾部
- 变量以及一些内存的管理
- Android中的设计模式
- 2016-7-11 杂感
- java的文件操作(1)
- 优先队列的使用
- ExtraViewWrapperAdapter--添加额外头部尾部功能的装饰adapter
- 注册表查看串口列表
- 存储过程1-用户登录实现
- maven创建spring项目之后,启动报错java.lang.ClassNotFoundException: org.springframework.web.**.**
- 链表
- FragmentActivity和Activity的具体区别在哪里
- 往ArrayList 传自定义对象并重写Equals()
- JavaScript学习笔记十九:标准对象-RegExp
- http请求加密