使用ListView的都知道ListView中有addHeaderView()和addFooterView()中有添加头部和底部的方法,而RecyclerView却没有,当然也可以自己定义,从网上找的代码在Adapter中添加,太过繁琐。经过分析ListView的实现方法,RecyclerView比着葫芦画瓢同样也能实现,使用更加简单。下面先分析一下ListView的实现。
一、ListView源码分析
先看一下ListView的addHeaderView()方法 源码:
/**
* Add a fixed view to appear at the top of the list. If this method is
* called more than once, the views will appear in the order they were
* added. Views added using this call can take focus if they want.
*
* Note: When first introduced, this method could only be called before
* setting the adapter with {@link #setAdapter(ListAdapter)}. Starting with
* {@link android.os.Build.VERSION_CODES#KITKAT}, this method may be
* called at any time. If the ListView's adapter does not extend
* {@link HeaderViewListAdapter}, it will be wrapped with a supporting
* instance of {@link WrapperListAdapter}.
*
* @param v The view to add.
* @param data Data to associate with this view
* @param isSelectable whether the item is selectable
*/
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();
}
}
}
我们可以看到这句代码:
if (!(mAdapter instanceof HeaderViewListAdapter)) {
mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, mAdapter);
}
不错多了HeaderViewListAdapter类,我们点进去看一下具体实现:
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 mHeaderViewInfos;
ArrayList mFooterViewInfos;
// Used as a placeholder in case the provided info views are indeed null.
// Currently only used by some CTS tests, which may be removed.
static final ArrayList EMPTY_INFO_LIST =
new ArrayList();
public HeaderViewListAdapter(ArrayList headerViewInfos,
ArrayList footerViewInfos,
ListAdapter adapter) {
......省略
mAdapter = adapter;
......省略
}
public int getHeadersCount() {
return mHeaderViewInfos.size();
}
public int getFootersCount() {
return mFooterViewInfos.size();
}
public boolean isEmpty() {
return mAdapter == null || mAdapter.isEmpty();
}
private boolean areAllListInfosSelectable(ArrayList infos) {
if (infos != null) {
for (ListView.FixedViewInfo info : infos) {
if (!info.isSelectable) {
return false;
}
}
}
return true;
}
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) {
return getFootersCount() + getHeadersCount() + mAdapter.getCount();
} else {
return getFootersCount() + getHeadersCount();
}
}
public boolean isEnabled(int position) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).isSelectable;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.isEnabled(adjPosition);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
}
public Object getItem(int position) {
// Header (negative positions will throw an IndexOutOfBoundsException)
int numHeaders = getHeadersCount();
if (position < numHeaders) {
return mHeaderViewInfos.get(position).data;
}
// Adapter
final int adjPosition = position - numHeaders;
int adapterCount = 0;
if (mAdapter != null) {
adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItem(adjPosition);
}
}
// Footer (off-limits positions will throw an IndexOutOfBoundsException)
return mFooterViewInfos.get(adjPosition - adapterCount).data;
}
// 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;
}
return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
}
public void registerDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.registerDataSetObserver(observer);
}
}
可以看出ListView也不是直接能添加头部和底部,系统封装好了一个包裹类,做了一系列处理,我们才能使用。所以我们也可以仿照它,自己动手封装一个包裹类。
二、自定义RecyclerView包裹类
2.创建可以添加头部和底部的WrapRecyclerAdapter
2.1 创建存放底部和头部View的集合 ,构造创建
private SparseArray mHeaderViews;
private SparseArray mFooterViews;
// 列表的Adapter
private RecyclerView.Adapter mAdapter;
// 基本的头部类型开始位置 用于viewType
private static int BASE_ITEM_TYPE_HEADER = 20000000;
// 基本的底部类型开始位置 用于viewType
private static int BASE_ITEM_TYPE_FOOTER = 30000000;
public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
mHeaderViews = new SparseArray<>();
mFooterViews = new SparseArray<>();
}
2.2创建添加和删除头部底部的方法
// 添加头部
public void addHeaderView(View view) {
int position = mHeaderViews.indexOfValue(view);
if (position < 0) {
mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
}
notifyDataSetChanged();
}
// 添加底部
public void addFooterView(View view) {
int position = mFooterViews.indexOfValue(view);
if (position < 0) {
mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, 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();
}
2.3onCreateViewHolder方法
// viewType 可能就是 SparseArray 的key
if (isHeaderViewType(viewType)) {
View headerView = mHeaderViews.get(viewType);
return createHeaderFooterViewHolder(headerView);
}
if (isFooterViewType(viewType)) {
View footerView = mFooterViews.get(viewType);
return createHeaderFooterViewHolder(footerView);
}
return mAdapter.onCreateViewHolder(parent, viewType);
其中判断是否是头部和底部的方法
/**
* 是不是底部类型
*/
private boolean isFooterViewType(int viewType) {
int position = mFooterViews.indexOfKey(viewType);
return position >= 0;
}
/**
* 是不是头部类型
*/
private boolean isHeaderViewType(int viewType) {
int position = mHeaderViews.indexOfKey(viewType);
return position >= 0;
}
创建头部或者底部的ViewHolder
/**
* 创建头部或者底部的ViewHolder
*/
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {
};
}
2.4onBindViewHolder方法
if (isHeaderPosition(position) || isFooterPosition(position)) {
return;
}
// 计算一下位置
final int adapterPosition = position - mHeaderViews.size();
mAdapter.onBindViewHolder(holder, adapterPosition);
// 设置点击和长按事件
if (mItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mItemClickListener.onItemClick(adapterPosition);
}
});
}
if (mLongClickListener != null) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return mLongClickListener.onLongClick(adapterPosition);
}
});
}
2.5getItemViewType方法
在onCreateViewHolder方法之前就会调用该方法
if (isHeaderPosition(position)) {
// 直接返回position位置的key
return mHeaderViews.keyAt(position);
}
if (isFooterPosition(position)) {
// 直接返回position位置的key
position = position - mHeaderViews.size() - mAdapter.getItemCount();
return mFooterViews.keyAt(position);
}
// 返回列表Adapter的getItemViewType
position = position - mHeaderViews.size();
return mAdapter.getItemViewType(position);
2.6 getItemCount方法
@Override
public int getItemCount() {
// 条数三者相加 = 底部条数 + 头部条数 + Adapter的条数
return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
}
三、创建可以添加头部和底部的RecyclerView
public class WrapRecyclerView extends RecyclerView {
// 包裹了一层的头部底部Adapter
private WrapRecyclerAdapter mWrapRecyclerAdapter;
// 这个是列表数据的Adapter
private RecyclerView.Adapter mAdapter;
public WrapRecyclerView(Context context) {
super(context);
}
public WrapRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
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);
}
super.setAdapter(mWrapRecyclerAdapter);
// 注册一个观察者
mAdapter.registerAdapterDataObserver(mDataObserver);
// 加载数据页面
if (mLoadingView != null && mLoadingView.getVisibility() == View.VISIBLE) {
mLoadingView.setVisibility(View.GONE);
}
if (mItemClickListener != null) {
mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
}
if (mLongClickListener != null) {
mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
}
}
// 添加头部
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);
}
}
private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
@Override
public void onChanged() {
if (mAdapter == null) return;
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyDataSetChanged();
dataChanged();
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
if (mAdapter == null) return;
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyDataSetChanged没效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemRemoved(positionStart);
dataChanged();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
if (mAdapter == null) return;
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemMoved没效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemMoved(fromPosition, toPosition);
dataChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
if (mAdapter == null) return;
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemChanged(positionStart);
dataChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (mAdapter == null) return;
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemChanged没效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemChanged(positionStart, payload);
dataChanged();
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
if (mAdapter == null) return;
// 观察者 列表Adapter更新 包裹的也需要更新不然列表的notifyItemInserted没效果
if (mWrapRecyclerAdapter != mAdapter)
mWrapRecyclerAdapter.notifyItemInserted(positionStart);
dataChanged();
}
};
/**
* Adapter数据改变的方法
*/
private void dataChanged() {
if (mAdapter.getItemCount() == 0) {
// 没有数据
if (mEmptyView != null) {
mEmptyView.setVisibility(VISIBLE);
} else {
mEmptyView.setVisibility(GONE);
}
}
}
/*
* 给条目设置点击和长按事件
*/
public OnItemClickListener mItemClickListener;
public com.hc.recycleranalysis.adapter.OnLongClickListener mLongClickListener;
public void setOnItemClickListener(OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener;
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
}
}
public void setOnLongClickListener(com.hc.recycleranalysis.adapter.OnLongClickListener longClickListener) {
this.mLongClickListener = longClickListener;
if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
}
}
}