如何为 RecyclerView 优雅地添加 Header、Fo
本文为菜鸟窝作者蒋志碧的连载。“从 0 开始开发一款直播 APP ”系列来聊聊时下最火的直播 APP,如何完整的实现一个类"腾讯直播"的商业化项目
视频地址:http://www.cniao5.com/course/10121
【从 0 开始开发一款直播 APP】3.1 高层封装之 Adapter — ListView & GridView
【从 0 开始开发一款直播 APP】3.2 高层封装之 Adapter — RecyclerView 实现单布局展示
【从 0 开始开发一款直播 APP】3.3 高层封装之 Adapter -- RecyclerView 实现多条目展示
【从 0 开始开发一款直播 APP】3.4 高层封装之 Adapter -- RecyclerView 优雅的添加 Header、Footer
一、优雅的添加 Header、Footer
上一章讲解了 RecyclerView 封装的多条目实现聊天界面,接下来讲解 RecyclerView 实现添加 Header、Footer。运行效果如下:
线性布局
网格布局
RecyclerView 系统没有封装添加头部和底部方法,但是 ListView 和 GridView 有封装,通过查看源码,可以类似封装一个添加头部和尾部的类。ListView 本身也不支持头部和尾部,是在 ListView 上做了一层包裹。
ListView 源码解析 -- ListView.addHeaderView(View v, Object data, boolean isSelectable)
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;
//判断 Adapter 是否为空,不判断无法添加头部
// Wrap the adapter if it wasn't already wrapped.
if (mAdapter != null) {
//判断是否有被包裹过
if (!(mAdapter instanceof HeaderViewListAdapter)) {
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();
}
}
}
**ListView 源码解析 -- **HeaderViewListAdapter.java
public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
private final ListAdapter mAdapter;
// These two ArrayList are assumed to NOT be null.
// They are indeed created when declared in ListView and then shared.
//存放头部和底部集合
ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
ListAdapter adapter) {
//原始列表 Adapter
mAdapter = adapter;
//......
}
//移除头部
public boolean removeHeader(View v) {
for (int i = 0; i < mHeaderViewInfos.size(); i++) {
ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
if (info.view == v) {
mHeaderViewInfos.remove(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
//移除底部
public boolean removeFooter(View v) {
for (int i = 0; i < mFooterViewInfos.size(); i++) {
ListView.FixedViewInfo info = mFooterViewInfos.get(i);
if (info.view == v) {
mFooterViewInfos.remove(i);
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
return true;
}
}
return false;
}
//获取条目
public int getCount() {
if (mAdapter != null) {
//头部条目 + Adapter 条目 + 底部条目
return getFootersCount() + getHeadersCount() + mAdapter.getCount();
} else {
return getFootersCount() + getHeadersCount();
}
}
//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;
}
// Adapter
final 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 和 RecyclerView 相似
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;
}
}
根据 ListView 的头部添加类,经过改写,可以得到我们自己的封装类,头部和底部可能有多个,因此需要用集合标识,这里使用 SparseArray。
public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
//不包含头部和底部
private RecyclerView.Adapter mAdapter;
//由于头部和底部可能有多个,需要用标识来识别
private int BASE_HEADER_KEY = 5500000;
private int BASE_Footer_KEY = 6600000;
//头部和底部集合 必须要用map集合进行标识 key->int value->object
SparseArray<View> mHeaderViews;
SparseArray<View> mFooterViews;
public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
mAdapter = adapter;
mHeaderViews = new SparseArray<>();
mFooterViews = new SparseArray<>();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//区分头部和底部,根据ViewType来
//ViewType可能有三部分 头部 底部 Adapter
//判断是否为头部
if (isHeaderViewType(viewType)) {
View headerView = mHeaderViews.get(viewType);
//header
return createHeaderOrFooterViewHolder(headerView);
}
//判断是否为底部
if (isFooterViewType(viewType)) {
View footerView = mFooterViews.get(viewType);
//footer
return createHeaderOrFooterViewHolder(footerView);
}
//列表
return mAdapter.onCreateViewHolder(parent, viewType);
}
/**
* 创建头部和底部ViewHolder
* @param view
* @return
*/
private RecyclerView.ViewHolder createHeaderOrFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {};
}
@Override
public int getItemViewType(int position) {
//position -> viewtype 头部 底部 adapter 必须要用map集合进行标识
//if(头部)return 头部 key
//if(中间位置)return mAdapter.getItemViewType(position);
//if(底部) return 底部 key
//header
if (isHeaderPosition(position)) {
return mHeaderViews.keyAt(position);
}
// footer
if (isFooterPosition(position)) {
position = position - mHeaderViews.size() - mAdapter.getItemCount();
return mFooterViews.keyAt(position);
}
//adapter
position = position - mHeaderViews.size();
return mAdapter.getItemViewType(position);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
//头部,底部不需要绑定数据
if (isHeaderPosition(position) || isFooterPosition(position)) {
return;
}
// Adapter
position = position - mHeaderViews.size();
mAdapter.onBindViewHolder(holder, position);
}
@Override
public int getItemCount() {
return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
}
//添加头部,底部
public void addHeaderView(View view) {
//没有包含头部
int position = mHeaderViews.indexOfValue(view);
if (position < 0) {
//集合没有就添加,不能重复添加
mHeaderViews.put(BASE_HEADER_KEY++, view);
}
notifyDataSetChanged();
}
public void addFooterView(View view) {
//没有包含头部
int position = mFooterViews.indexOfValue(view);
if (position < 0) {
//集合没有就添加,不能重复添加
mFooterViews.put(BASE_Footer_KEY++, view);
}
notifyDataSetChanged();
}
//移除头部,底部
public void removeHeaderView(View view) {
//没有包含头部
int index = mHeaderViews.indexOfValue(view);
if (index < 0) return;
//集合没有就添加,不能重复添加
mHeaderViews.removeAt(index);
notifyDataSetChanged();
}
public void removeFooterView(View view) {
//没有包含底部
int index = mFooterViews.indexOfValue(view);
if (index < 0) return;
//集合没有就添加,不能重复添加
mFooterViews.removeAt(index);
notifyDataSetChanged();
}
//是否为底部
private boolean isFooterViewType(int viewType) {
int footerPosition = mFooterViews.indexOfKey(viewType);
return footerPosition >= 0;
}
//是否为头部
private boolean isHeaderViewType(int viewType) {
int headerPosition = mHeaderViews.indexOfKey(viewType);
return headerPosition >= 0;
}
//是否为底部位置
private boolean isFooterPosition(int position) {
return position >= (mHeaderViews.size() + mAdapter.getItemCount());
}
//是否为头部位置
private boolean isHeaderPosition(int position) {
return position < mHeaderViews.size();
}
}
二、线性布局添加 Header、Footer
public class AdapterActivity extends BaseActivity {
private MutipleAdaper mMutipleAdaper;
private WrapRecyclerAdapter mWrapRecyclerAdapter;
private RecyclerView mRecyclerView;
@Override
protected void setToolbar() {
}
@Override
protected void setListener() {
}
@Override
protected void initData() {
Datas = new ArrayList<>();
for (int i = 1; i <= 30; i++) {
if (i % 2 == 0) {
Datas.add(new Item(R.drawable.tab_publish_normal,"我 get 新技能 " + i,0));
}else {
Datas.add(new Item(R.drawable.tab_publish_normal,"你 get 新技能 " + i,1));
}
}
mMutipleAdaper = new MutipleAdaper(this,Datas);
mWrapRecyclerAdapter = new WrapRecyclerAdapter(mMutipleAdaper);
mRecyclerView.setAdapter(mWrapRecyclerAdapter);
View headerView = LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false);
View footView = LayoutInflater.from(this).inflate(R.layout.layout_footer,mRecyclerView,false);
mWrapRecyclerAdapter.addHeaderView(headerView);
mWrapRecyclerAdapter.addFooterView(footView);
}
@Override
protected void initView() {
mRecyclerView = obtainView(R.id.recyclerView);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
}
@Override
protected int getLayoutId() {
return R.layout.activity_adapter;
}
}
三、运行效果图
四、WrapRecyclerView -- 自定义 RecyclerView
可以看到上面使用头部Adapter很不友好,需要将之前封装的布局添加到包裹 Adapter 中,可以将自定义一个 RecyclerView,让其继承 RecyclerView,将 WrapRecyclerAdapter 传入,调用其方法。
public class WrapRecyclerView extends RecyclerView {
private WrapRecyclerAdapter mWrapRecyclerAdapter;
private RecyclerView.Adapter mAdapter;
public WrapRecyclerView(Context context) {
this(context, null);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setAdapter(Adapter adapter) {
//防止多次设置Adapter
if (mAdapter != null) {
mAdapter = null;
}
this.mAdapter = adapter;
if (adapter instanceof WrapRecyclerAdapter) {
mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
} else {
mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
}
//删除的问题是列表的Adapter改变,但WrapRecyclerAdapter没有改,观察者模式
super.setAdapter(mWrapRecyclerAdapter);
}
//添加头部,底部
public void addHeaderView(View view) {
//如果没有Adapter就不添加,也可以抛异常
//必须先设置Adapter才能添加,仿照ListView的处理方式
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addHeaderView(view);
}
}
public void addFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addFooterView(view);
}
}
//移除头部,底部
public void removeHeaderView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeHeaderView(view);
}
}
public void removeFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeFooterView(view);
}
}
}
使用的时候其它不变,只需要稍微修改
五、WrapRecyclerView 的使用
布局文件不能使用 RecyclerView,使用自定义的 WrapRecyclerView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.dali.admin.fragment.AdapterActivity">
<!--<android.support.v7.widget.RecyclerView-->
<!--android:id="@+id/recyclerView"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="wrap_content">-->
<!--</android.support.v7.widget.RecyclerView>-->
<com.dali.admin.fragment.recycler.WrapRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"></com.dali.admin.fragment.recycler.WrapRecyclerView>
</LinearLayout>
在AdapterActivity 中只需要将 WrapRecyclerView 声明,直接调用 addHeaderView() 就可以添加头部,添加底部类似
private WrapRecyclerView mRecyclerView;
View headerView = LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false);
View footerView = LayoutInflater.from(this).inflate(R.layout.layout_footer,mRecyclerView,false);
mRecyclerView.addHeaderView(headerView);
mRecyclerView.addFooterView(footerView);
看到这里,基本封装就完了,细心的朋友会发现没有封装监听,移除头部和底部很简单,直接添加监听就行
headerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRecyclerView.removeHeaderView(v);
}
});
footerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRecyclerView.removeFooterView(v);
}
});
但是问题来了,我们在 ViewHolder 添加监听,Adapter 条目不会移除。怎么点都没反应。
public class MutipleAdaper extends RecyclerViewAdapter<Item> {
public MutipleAdaper(Context context, List<Item> datas) {
super(context, datas, new MutipleTypeSupport<Item>() {
@Override
public int getLayoutId(Item item) {
if (item.getType() == 1){
return R.layout.list_item;
}else {
return R.layout.list_item1;
}
}
});
}
@Override
protected void bindData(RecyclerViewHolder holder, final Item item, int position) {
holder.setText(R.id.tv1,item.getTv1())
.setImageResource(R.id.img,item.getRes())
.setOnClickListener(R.id.tv1, new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext,item.getTv1(),Toast.LENGTH_SHORT).show();
mDatas.remove(item); //移除条目 item
notifyDataSetChanged();
}
});
}
}
原因是因为我们操作的 Adapter 不是同一个,点击的条目 Adapter 是 MutipleAdaper,WrapRecyclerView 里面WrapRecyclerAdapter 没有改变,因此没有实现同步更新,这里需要使用到观察者模式。
我们可以在 ListView 的 HeaderViewListAdapter
源码里也看到有使用观察者模式。
public void registerDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.registerDataSetObserver(observer);
}
}
public void unregisterDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(observer);
}
}
我们也需要为其添加观察者模式
,使通知到每个 Adapter,保证数据同步更新。否则列表的notifyDataSetChanged() 没效果
public class WrapRecyclerView extends RecyclerView {
private WrapRecyclerAdapter mWrapRecyclerAdapter;
private RecyclerView.Adapter mAdapter;
//重写观察者类
private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyDataSetChanged();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemChanged(positionStart);
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
if (mAdapter == null) return;
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemInserted(positionStart);
}
};
public WrapRecyclerView(Context context) {
this(context, null);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void setAdapter(Adapter adapter) {
//防止多次设置Adapter
if (mAdapter != null) {//注销观察者
mAdapter.unregisterAdapterDataObserver(mDataObserver);
mAdapter = null;
}
this.mAdapter = adapter;
if (adapter instanceof WrapRecyclerAdapter) {
mWrapRecyclerAdapter = (WrapRecyclerAdapter) adapter;
} else {
mWrapRecyclerAdapter = new WrapRecyclerAdapter(adapter);
}
//删除的问题是列表的 Adapter 改变,但 WrapRecyclerAdapter没有改,观察者模式
super.setAdapter(mWrapRecyclerAdapter);
//注册观察者模式
mAdapter.registerAdapterDataObserver(mDataObserver);
}
//添加头部,底部
public void addHeaderView(View view) {
//如果没有Adapter就不添加,也可以抛异常
//必须先设置Adapter才能添加,仿照ListView的处理方式
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addHeaderView(view);
}
}
public void addFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.addFooterView(view);
}
}
//移除头部,底部
public void removeHeaderView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeHeaderView(view);
}
}
public void removeFooterView(View view) {
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.removeFooterView(view);
}
}
}
六、完整的代码
public class AdapterActivity extends BaseActivity {
private MutipleAdaper mMutipleAdaper;
private WrapRecyclerView mRecyclerView;
private ArrayList<Item> Datas;
@Override
protected void setToolbar() {
}
@Override
protected void setListener() {
}
@Override
protected void initData() {
Datas = new ArrayList<>();
for (int i = 1; i <= 30; i++) {
if (i % 2 == 0) {
Datas.add(new Item(R.drawable.tab_publish_normal,"我 get 新技能 " + i,0));
}else {
Datas.add(new Item(R.drawable.tab_publish_normal,"你 get 新技能 " + i,1));
}
}
mMutipleAdaper = new MutipleAdaper(this,Datas);
mRecyclerView.setAdapter(mMutipleAdaper);
View headerView = LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false);
View footerView = LayoutInflater.from(this).inflate(R.layout.layout_footer,mRecyclerView,false);
mRecyclerView.addHeaderView(headerView);
headerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRecyclerView.removeHeaderView(v);
}
});
mRecyclerView.addFooterView(footerView);
footerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mRecyclerView.removeFooterView(v);
}
});
}
@Override
protected void initView() {
mRecyclerView = obtainView(R.id.recyclerView);
mRecyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST));
mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
}
@Override
protected int getLayoutId() {
return R.layout.activity_adapter;
}
}
再次使用点击事件并运行
七、网格布局添加 Header、Footer
可以看到基本功能已经实现,但是这里只有 LinearLayout 的布局,我们运行 GridLayout 布局,并为其添加头部和底部。只需要改写布局管理器就好
mRecyclerView.addItemDecoration(new DividerGridItemDecoration(this));
mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));
可以看到是这种效果,并不是独占一行,为解决此问题,要在原布局添加一个方法判断是否是 GridLayoutManager,如果是,对其头部和底部显示做处理。
public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/**
* 解决GridLayoutManager添加头部和底部不占用一行的问题
* @param recycler
*/
public void adjustSpanSize(RecyclerView recycler) {
if (recycler.getLayoutManager() instanceof GridLayoutManager) {
final GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
boolean isHeaderOrFooter = isHeaderPosition(position) || isFooterPosition(position);
return isHeaderOrFooter ? layoutManager.getSpanCount() : 1;
}
});
}
}
}
此方法要在封装的RecyclerView中调用
public class WrapRecyclerView extends RecyclerView {
@Override
public void setAdapter(Adapter adapter) {
//解决GridLayout添加头部和底部也要占据一行
mWrapRecyclerAdapter.adjustSpanSize(this);
}
}
再次运行
总结
对于 RecyclerView 的 Adapter 的封装。
单条目的封装步骤
1、 ViewHolder 的封装,继承 RecyclerView.ViewHolder,实现其方法和构造函数
2、封装 ViewHolder 的 getView() 方法,避免 findViewById 及类型转换,提供设置控件和监听的一系列方法
3、Adapter 的封装,继承 RecyclerView.Adapter 类,将封装的 ViewHolder 作为范型传入,将数据源作为范型传入。如:BaseAdapter<T> extends RecyclerView.Adapter<BaseViewHolder>。实现其构造方法和抽象方法。
4、设置数据源,上下文对象,布局 ID,抽象绑定 ViewHolder 具体实现,将 ViewHolder 和数据源作为参数传递出去,让用户对数据进行相应处理。
多条目的封装
1、在单条目基础上通过构造函数传递一个提供布局的接口,通过 ViewType 类型判断到底使用什么布局。
2、在 getItemViewType() 方法中判断布局类型,在 onCreateViewHolder() 方法中判断是否需要多布局,根据用户需要实现不同的构造函数
头部和底部添加
1、查看 ListView 的 addHeaderView() 及其相关源码,了解添加头部和底部的具体思路,根据其封装进行编写我们自己的方法。
2、头部和底部考虑使用容器存储,且需要进行标识,采用 android 为我们提供的 SparseArray 进行存储,范型为 View 类型。
3、getItemViewType() 方法根据 position 进行判断是头部、底部还是条目Adapter。
3、onCreateViewHolder() 方法是构造 ViewHolder 的,此处应该有三类 ViewHolder,头部,底部,条目 Adapter,根据 ViewType 来进行判断是什么类型的 ViewHolder,并相应的进行构造。
4、onBindViewHolder() 方法是绑定条目数据的,头部和底部是不需要绑定数据的,因此需要将其排除在外,在对条目 Adapter 进行绑定数据。
5、最后添加 addHeaderView()、addFooterView()、removeHeaderView()、removeFooterView() 等方法,封装算大致完毕。
6、对 RecyclerView 进行重写,其实就是对 RecyclerView 进行一层包裹,将封装的可添加头部和底部的 Adapter 声明,并调用其方法就行了,形成和 ListView 一样可以直接添加头部和底部的自定义控件。
7、要实现真正的点击同步,需要使用观察者模式,实现 Adapter 操作通知同步。
8、对于 GridView 添加头部不能独占一行进行相应处理。
对于 RecyclerView 的封装讲到此,有不足的地方欢迎指正。
关注菜鸟窝官网,免费领取140套开源项目
菜鸟窝官网公号二维码.png加入菜鸟直播—实战app交流群
微信图片_20170807101053.png