概述
1、目的:为RecyclerView添加头部和底部视图
2、分析:RecyclerView在使用的过程中,没有发现想ListView中addHeadView()、addFooterView()的方法
3、实现思路:模仿ListView的方式实现,研究ListView的添加头部和底部视图的实现源码,依照同样的模式实现
ListView添加头部和底部的功能源码解析
1、查看一下ListView的addHeaderView()方法:
从如下的方法中查看关键信息,会看到mHeaderViewInfos.add(info);就是将传入的参数view通过mHeaderViewInfos进行存储,存放要添加的headerview
接着看到一个判断语句 if (!(mAdapter instanceof HeaderViewListAdapter)),这个判断中有一个方法wrapHeaderListAdapterInternal();进去看到的是mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
意思是如果这个mAdapte不是HeaderViewListAdapter,那么就把它转化成一个HeaderViewListAdapter,同时把这个原本的mAdapter通过构造方法传递到HeaderViewListAdapter这个类中,传入adapter的方式肯定是setAdapter方法,接着看setAdapter
addHeaderView源码:
publicvoidaddHeaderView(View v, Object data,booleanisSelectable){
if(v.getParent() !=null&& v.getParent() !=this) {
if(Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG,"The specified child already has a parent. "
+"You must call removeView() on the child's parent first.");
}
}
finalFixedViewInfo info =newFixedViewInfo();
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(!(mAdapterinstanceofHeaderViewListAdapter)) {
wrapHeaderListAdapterInternal();
}
// 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();
}
}
}
protectedvoidwrapHeaderListAdapterInternal(){
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
2、查看一下ListView的setAdapter()方法:
从这个方法看到如下关键代码块:
if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {
mAdapter= wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
}else{
mAdapter= adapter;
}
就是说setAdapter会先去判断是否你前面添加过headerView或者添加过footerView,以此来判断应该给你转化为一个HeaderViewListAdapter还是直接给你 mAdapter = adapter。
这里体现了偷梁换柱的概念,就是说你添加了headerView或者footerView就用一个新的Adapter来替代你传入的adapter,这个在设计模式上使用了装饰设计模式,接下来查看一下HeaderViewListAdapter
setAdapter源码:
@Override
publicvoidsetAdapter(ListAdapter adapter){
if(mAdapter !=null&& mDataSetObserver !=null) {
mAdapter.unregisterDataSetObserver(mDataSetObserver);
}
resetList();
mRecycler.clear();
if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {
mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
}else{
mAdapter = adapter;
}
mOldSelectedPosition = INVALID_POSITION;
mOldSelectedRowId = INVALID_ROW_ID;
// AbsListView#setAdapter will update choice mode states.
super.setAdapter(adapter);
if(mAdapter !=null) {
mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
mOldItemCount = mItemCount;
mItemCount = mAdapter.getCount();
checkFocus();
mDataSetObserver =newAdapterDataSetObserver();
mAdapter.registerDataSetObserver(mDataSetObserver);
mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());
intposition;
if(mStackFromBottom) {
position = lookForSelectablePosition(mItemCount -1,false);
}else{
position = lookForSelectablePosition(0,true);
}
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
if(mItemCount ==0) {
// Nothing selected
checkSelectionChanged();
}
}else{
mAreAllItemsSelectable =true;
checkFocus();
// Nothing selected
checkSelectionChanged();
}
requestLayout();
}
3、查看HeaderViewListAdapter关键源码:
分析看到getView()方法有三处判断,分别是返回了三种视图,头部视图、内容视图、底部视图;发现头部视图和底部视图专门处理,内容视图直接复用了传入的adapter的getView()方法。
publicViewgetView(intposition, View convertView, ViewGroup parent){
// Header (negative positions will throw an IndexOutOfBoundsException)
intnumHeaders = getHeadersCount();
if(position < numHeaders) {
returnmHeaderViewInfos.get(position).view;
}
// Adapter
finalintadjPosition = position - numHeaders;
intadapterCount =0;
if(mAdapter !=null) {
adapterCount = mAdapter.getCount();
if(adjPosition < adapterCount) {
returnmAdapter.getView(adjPosition, convertView, parent);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
returnmFooterViewInfos.get(adjPosition - adapterCount).view;
}
publicintgetItemViewType(intposition){
intnumHeaders = getHeadersCount();
if(mAdapter !=null&& position >= numHeaders) {
intadjPosition = position - numHeaders;
intadapterCount = mAdapter.getCount();
if(adjPosition < adapterCount) {
returnmAdapter.getItemViewType(adjPosition);
}
}
returnAdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
综上分析,要实现RecyleView添加头部和底部功能,
1、需要自定义一个RecyleView,添加addHeaerView()、addFooterView()
2、其次自定义一个HeaderViewRecyleViewAdapter,用于实现添加头部和底部视图的展示
自定义RecyleView实现WrapRecyleView类:
publicclassWrapRecyleViewextendsRecyclerView{
privateArrayList mHeaderViewInfos =newArrayList<>();
privateArrayList mFooterViewInfos =newArrayList<>();
privateAdapter mAdapter;
publicWrapRecyleView(Context context,AttributeSet attrs){
super(context, attrs);
}
publicvoidaddHeaderView(View v){
mHeaderViewInfos.add(v);
// Wrap the adapter if it wasn't already wrapped.
if(mAdapter !=null) {
if(!(mAdapterinstanceofHeaderViewRecyleViewAdapter)) {
mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
publicvoidaddFooterView(View v){
mFooterViewInfos.add(v);
// Wrap the adapter if it wasn't already wrapped.
if(mAdapter !=null) {
if(!(mAdapterinstanceofHeaderViewRecyleViewAdapter)) {
mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
}
}
@Override
publicvoidsetAdapter(Adapter adapter){
if(mHeaderViewInfos.size() >0|| mFooterViewInfos.size() >0) {
mAdapter =newHeaderViewRecyleViewAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
}else{
mAdapter = adapter;
}
super.setAdapter(mAdapter);
}
}
HeaderViewRecyleViewAdapter类实现:
publicclassHeaderViewRecyleViewAdapterextendsRecyclerView.Adapter{
privateArrayList mHeaderViewInfos;
privateArrayList mFooterViewInfos;
privateRecyclerView.Adapter mAdapter;
publicHeaderViewRecyleViewAdapter(ArrayList headerViewInfos, ArrayList footerViewInfos, RecyclerView.Adapter adapter){
mAdapter = adapter;
if(headerViewInfos ==null) {
mHeaderViewInfos =newArrayList<>();
}else{
mHeaderViewInfos = headerViewInfos;
}
if(footerViewInfos ==null) {
mFooterViewInfos =newArrayList<>();
}else{
mFooterViewInfos = footerViewInfos;
}
}
@NonNull
@Override
publicRecyclerView.ViewHolderonCreateViewHolder(@NonNull ViewGroup parent,intviewType){
//header
if(viewType==RecyclerView.INVALID_TYPE){
returnnewHeaderViewHolder(mHeaderViewInfos.get(0));
}elseif(viewType==RecyclerView.INVALID_TYPE-1){//footer
returnnewHeaderViewHolder(mFooterViewInfos.get(0));
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
returnmAdapter.onCreateViewHolder(parent, viewType);
}
@Override
publicvoidonBindViewHolder(@NonNull RecyclerView.ViewHolder holder,intposition){
//划分头部、正常部分、尾部
intnumHeaders = getHeadersCount();
//头部
if(position < numHeaders) {
return;
}
//adapter bod正常部分
finalintadjPosition = position - numHeaders;
intadapterCount =0;
if(mAdapter !=null) {
adapterCount = mAdapter.getItemCount();
if(adjPosition < adapterCount) {
mAdapter.onBindViewHolder(holder, adjPosition);
return;
}
}
//footer
}
@Override
publicintgetItemCount(){
if(mAdapter !=null) {
returngetFootersCount() + getHeadersCount() + mAdapter.getItemCount();
}else{
returngetFootersCount() + getHeadersCount();
}
}
@Override
publicintgetItemViewType(intposition){
//判断当前条目是什么类型,就渲染什么视图给什么数据
intnumHeaders = getHeadersCount();
//头部
if(position < numHeaders) {
returnRecyclerView.INVALID_TYPE;
}
//正常条目部分
// Adapter
finalintadjPosition = position - numHeaders;
intadapterCount =0;
if(mAdapter !=null) {
adapterCount = mAdapter.getItemCount();
if(adjPosition < adapterCount) {
returnmAdapter.getItemViewType(adjPosition);
}
}
//footer部分
returnRecyclerView.INVALID_TYPE-1;
}
privatestaticclassHeaderViewHolderextendsRecyclerView.ViewHolder{
publicHeaderViewHolder(View view){
super(view);
}
}
publicintgetHeadersCount(){
returnmHeaderViewInfos.size();
}
publicintgetFootersCount(){
returnmFooterViewInfos.size();
}
}
接下来就是应用,需要使用WrapRecyleView 来代替RecyleView ,其它都是正常的实现
setContentView(R.layout.activity_rvheader);
recyclerView = (WrapRecyleView) findViewById(R.id.recyclerView);
addHeaderView();
addFooterView();
List list =newArrayList<>();
for(inti =0; i <6; i++) {
list.add("item "+i);
}
MyAdapter adapter =newMyAdapter(list);
recyclerView.setLayoutManager(newLinearLayoutManager(this));
recyclerView.setAdapter(adapter);
效果图如下:
效果图如下:
源码地址:https://github.com/heiyl/recyleview
微信公众号:
图注:Android进化之路公众号