改进版:RecyclerView 通用Adapter添加头部、尾部 参考ListView的源码实现(二)
首先我们看下以前的ListView是如何实现addHeaderView、addFooterView
1.先看ListView.setAdapter(ListAdapter adapter) 方法
2.再看addHeaderView、addFooterView方法
以上2个方法中都用到了wrapHeaderListAdapterInternal方法,见下图
现在我们可以看出最终实现添加头部、尾部View,原来是靠HeaderViewListAdapter(包装后的Adapter)。
贴一下源码:
package android.widget;
import android.database.DataSetObserver;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**
* ListAdapter used when a ListView has header views. This ListAdapter
* wraps another one and also keeps track of the header views and their
* associated data objects.
*<p>This is intended as a base class; you will probably not need to
* use this class directly in your own code.
*/
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;
// 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<ListView.FixedViewInfo> EMPTY_INFO_LIST =
new ArrayList<ListView.FixedViewInfo>();
boolean mAreAllFixedViewsSelectable;
private final boolean mIsFilterable;
public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
ArrayList<ListView.FixedViewInfo> footerViewInfos,
ListAdapter adapter) {
mAdapter = adapter;
mIsFilterable = adapter instanceof Filterable;
if (headerViewInfos == null) {
mHeaderViewInfos = EMPTY_INFO_LIST;
} else {
mHeaderViewInfos = headerViewInfos;
}
if (footerViewInfos == null) {
mFooterViewInfos = EMPTY_INFO_LIST;
} else {
mFooterViewInfos = footerViewInfos;
}
mAreAllFixedViewsSelectable =
areAllListInfosSelectable(mHeaderViewInfos)
&& areAllListInfosSelectable(mFooterViewInfos);
}
public int getHeadersCount() {
return mHeaderViewInfos.size();
}
public int getFootersCount() {
return mFooterViewInfos.size();
}
public boolean isEmpty() {
return mAdapter == null || mAdapter.isEmpty();
}
private boolean areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> 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 areAllItemsEnabled() {
if (mAdapter != null) {
return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
} else {
return true;
}
}
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;
}
public long getItemId(int position) {
int numHeaders = getHeadersCount();
if (mAdapter != null && position >= numHeaders) {
int adjPosition = position - numHeaders;
int adapterCount = mAdapter.getCount();
if (adjPosition < adapterCount) {
return mAdapter.getItemId(adjPosition);
}
}
return -1;
}
public boolean hasStableIds() {
if (mAdapter != null) {
return mAdapter.hasStableIds();
}
return false;
}
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;
}
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;
}
public int getViewTypeCount() {
if (mAdapter != null) {
return mAdapter.getViewTypeCount();
}
return 1;
}
public void registerDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.registerDataSetObserver(observer);
}
}
public void unregisterDataSetObserver(DataSetObserver observer) {
if (mAdapter != null) {
mAdapter.unregisterDataSetObserver(observer);
}
}
public Filter getFilter() {
if (mIsFilterable) {
return ((Filterable) mAdapter).getFilter();
}
return null;
}
public ListAdapter getWrappedAdapter() {
return mAdapter;
}
}
我们只需重点看
getCount()
getItem()
getView()
getItemViewType()
getViewTypeCount()
这些方法的实现,上面的代码很简单应该都能看得懂,主要原理就是封装、化零为整思想,不关心未来入参的Adapter是怎样的,把它看做基类Adapter的一员,根据postion来智能地调用。
那么现在都是用RecyclerView来替代ListView了,但是RecyclerView本身是没提供addHeaderView、addFooterView方法,因为它是一个开放性、扩展性强的组件,更多的需要自定义实现。
附上源码
public class ListViewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
RecyclerView recyclerView = new RecyclerView(this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
InnerAdapter innerAdapter = new InnerAdapter();
RecyclerBaseAdapter baseAdapter = new RecyclerBaseAdapter(innerAdapter);
baseAdapter.setHeaderAdapter(new HeaderAdapter());
baseAdapter.setFooterAdapter(new FooterAdapter());
recyclerView.setAdapter(baseAdapter);
addContentView(recyclerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new HeadViewHoloder(LayoutInflater.from(ListViewActivity.this).inflate(android.R.layout.simple_list_item_1, null, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
HeadViewHoloder holoder_ = (HeadViewHoloder) holder;
((TextView) holoder_.itemView).setText("我是头部" + position);
}
@Override
public int getItemCount() {
return 2;
}
class HeadViewHoloder extends RecyclerView.ViewHolder {
public HeadViewHoloder(View itemView) {
super(itemView);
}
}
}
class InnerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new InnerViewHoloder(LayoutInflater.from(ListViewActivity.this).inflate(android.R.layout.simple_list_item_1, null, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
InnerViewHoloder holoder_ = (InnerViewHoloder) holder;
((TextView) holoder_.itemView).setText("我是正常Item" + position);
}
@Override
public int getItemCount() {
return 8;
}
class InnerViewHoloder extends RecyclerView.ViewHolder {
public InnerViewHoloder(View itemView) {
super(itemView);
}
}
}
class FooterAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new FooterViewHoloder(LayoutInflater.from(ListViewActivity.this).inflate(android.R.layout.simple_list_item_1, null, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
FooterViewHoloder holoder_ = (FooterViewHoloder) holder;
((TextView) holoder_.itemView).setText("我是尾部" + position);
}
@Override
public int getItemCount() {
return 3;
}
class FooterViewHoloder extends RecyclerView.ViewHolder {
public FooterViewHoloder(View itemView) {
super(itemView);
}
}
}
class RecyclerBaseAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private RecyclerView.Adapter mInnerAdapter;
private RecyclerView.Adapter headerAdapter;
private RecyclerView.Adapter footerAdapter;
public static final int ITEM_TYPE_FOOTER = Integer.MAX_VALUE - 2;
public static final int ITEM_TYPE_HEAD = Integer.MAX_VALUE - 1;
public boolean isHasHeader() {
return headerAdapter != null;
}
public boolean isHasFooter() {
return footerAdapter != null;
}
public void setHeaderAdapter(RecyclerView.Adapter hasHeader) {
this.headerAdapter = hasHeader;
}
public void setFooterAdapter(RecyclerView.Adapter footerAdapter) {
this.footerAdapter = footerAdapter;
}
public RecyclerBaseAdapter(RecyclerView.Adapter mInnerAdapter) {
this.mInnerAdapter = mInnerAdapter;
}
private boolean isHeaderViewType(int position) {
return isHasHeader() && position <headerAdapter.getItemCount();
}
private boolean isFooterViewType(int position) {
return isHasFooter() && (position >= mInnerAdapter.getItemCount() + (isHasHeader() ? headerAdapter.getItemCount() : 0));
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE_HEAD) {
return headerAdapter.onCreateViewHolder(parent, viewType);
} else if (viewType == ITEM_TYPE_FOOTER) {
return footerAdapter.onCreateViewHolder(parent, viewType);
} else {
return mInnerAdapter.onCreateViewHolder(parent, viewType);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ITEM_TYPE_HEAD:
headerAdapter.onBindViewHolder(holder, position);
break;
case ITEM_TYPE_FOOTER:
footerAdapter.onBindViewHolder(holder, position - mInnerAdapter.getItemCount() - (isHasHeader() ? headerAdapter.getItemCount() : 0));
break;
default:
mInnerAdapter.onBindViewHolder(holder, position - (isHasHeader() ? headerAdapter.getItemCount() : 0));
break;
}
}
@Override
public int getItemCount() {
return mInnerAdapter.getItemCount() + (isHasFooter() ? footerAdapter.getItemCount() : 0) + (isHasHeader() ? headerAdapter.getItemCount() : 0);
}
@Override
public int getItemViewType(int position) {
if (isHeaderViewType(position)) {
return ITEM_TYPE_HEAD;
} else if (isFooterViewType(position)) {
return ITEM_TYPE_FOOTER;
}
return this.mInnerAdapter.getItemViewType(position - (isHasHeader() ? headerAdapter.getItemCount() : 0));
}
}
}
RecyclerBaseAdapter 就是根据getItemViewType实现的通用Adapter,这样就可以愉快地添加头部、尾部了。