Android 解析RecyclerView(2)——带顶部View和底部View的RecyclerView
2017-06-07 08:28
531 查看
RecyclerView是用来替代ListView的一个控件,比ListView更加的简洁高效,不过也有一些比较不足的地方,比如:无法直接设置点击事件监听; 无法像ListView那样直接添加顶部View和底部View。
设置点击事件监听在前一篇文章已经解决了,这一篇文章要来介绍如何为RecyclerView添加顶部View和底部View。
可以看到,addHeaderView() 方法首先是将 headerView 保存到 FixedViewInfo 对象中,再将 FixedViewInfo 对象保存到集合中。
FixedViewInfo 类的定义如下所示:
mAdapter 即是为ListView设置的Adapter 对象,如果 mAdapter 不是HeaderViewListAdapter 的直接实例,则调用 wrapHeaderListAdapterInternal() 方法,以顶部View、底部View和mAdapter为参数,来将 mAdapter 转为 HeaderViewListAdapter 对象
查看 HeaderViewListAdapter 类的一些方法,可以看出 HeaderViewListAdapter 在计算子项总数和获取子项实例时,都是将顶部View和底部View包含进来的。
经过以上的分析,就给我们提供了设计思路。我们同样可以设计一个包裹了 RecyclerView Adapter 和顶部底部View的外部Adapter,再将该Adapter设置给 RecyclerView。此外,由于原生的 RecyclerView 没有对应的 addHeaderView() 和 addFooterView() 方法,所以也需要再来继承 RecyclerView,在子类中定义需要的方法。
然后声明几个用来添加和移除View的方法
重点是 getItemViewType() 方法,如果索引值position指向的是顶部View或者底部View,则返回该View在 SparseArray< View >中的Key值,以该值作为View的 ItemViewType。如果索引指向的是中间的展示数据的子项,则调用adapter本身相同的方法
为不同的View指定了不同的ItemViewType后,则可以在onCreateViewHolder() 方法中返回不同的 ViewHolder 对象了
总的方法定义如下所示:
首先需要声明两个变量
仿照ListView的思路来重写 setAdapter() 方法,构建一个 WrapRecyclerViewAdapter 对象作为实际的Adapter。
当中,需要注意的是,如果传入的 recyclerAdapter 是直接继承于 WrapViewRecycleAdapter 的话,则直接强转类型就可以了,否则的话需要再来根据 recyclerAdapter 构造一个 WrapViewRecycleAdapter 对象
如果 wrapRecyclerViewAdapter 是通过强转得来的,则当 mRecyclerAdapter 数据刷新时,wrapRecyclerViewAdapter 自然也会做出相应的变化,因为两者指向的是同一个对象。
如果 wrapRecyclerViewAdapter 是用new关键字重新声明的,则需要在为 mRecyclerAdapter 注册一个观察者对象,在 mRecyclerAdapter 数据刷新时同时通知 wrapRecyclerViewAdapter 也进行数据刷新。
总的代码如下所示:
在布局文件中声明的RecyclerView就要使用自定义的 WrapRecyclerView 了,再增加几个按钮用于进行RecyclerView展示的数据,顶部View和底部View的增添删除操作。
Activity总的代码如下所示,加载的顶部View布局文件 R.layout.header_view 只包含含一个 ImageView 控件,底部View布局文件 R.layout.footer _view 只包含一个 TextView 控件
运行效果:
设置点击事件监听在前一篇文章已经解决了,这一篇文章要来介绍如何为RecyclerView添加顶部View和底部View。
一、源码分析
先来看下ListView的源码,研究它是如何添加顶部View的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)) { 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(); } } }
可以看到,addHeaderView() 方法首先是将 headerView 保存到 FixedViewInfo 对象中,再将 FixedViewInfo 对象保存到集合中。
FixedViewInfo 类的定义如下所示:
public class FixedViewInfo { /** The view to add to the list */ public View view; /** The data backing the view. This is returned from {@link ListAdapter#getItem(int)}. */ public Object data; /** <code>true</code> if the fixed view should be selectable in the list */ public boolean isSelectable; }
mAdapter 即是为ListView设置的Adapter 对象,如果 mAdapter 不是HeaderViewListAdapter 的直接实例,则调用 wrapHeaderListAdapterInternal() 方法,以顶部View、底部View和mAdapter为参数,来将 mAdapter 转为 HeaderViewListAdapter 对象
protected void wrapHeaderListAdapterInternal() { mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, mAdapter); } protected HeaderViewListAdapter wrapHeaderListAdapterInternal( ArrayList<ListView.FixedViewInfo> headerViewInfos, ArrayList<ListView.FixedViewInfo> footerViewInfos, ListAdapter adapter) { return new HeaderViewListAdapter(headerViewInfos, footerViewInfos, adapter); }
查看 HeaderViewListAdapter 类的一些方法,可以看出 HeaderViewListAdapter 在计算子项总数和获取子项实例时,都是将顶部View和底部View包含进来的。
public int getCount() { if (mAdapter != null) { return getFootersCount() + getHeadersCount() + mAdapter.getCount(); } else { return getFootersCount() + getHeadersCount(); } } 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; }
经过以上的分析,就给我们提供了设计思路。我们同样可以设计一个包裹了 RecyclerView Adapter 和顶部底部View的外部Adapter,再将该Adapter设置给 RecyclerView。此外,由于原生的 RecyclerView 没有对应的 addHeaderView() 和 addFooterView() 方法,所以也需要再来继承 RecyclerView,在子类中定义需要的方法。
二、自定义Adapter
先定义几个需要用到的变量,headerViews 和 footerViews 分别用于存储顶部View和底部View,两个整数值则是会不断自增加一,用来作为SparseArray< View > 的Key值,adapter则是指向为RecyclerView设置的Adapterprivate RecyclerView.Adapter adapter; private SparseArray<View> headerViews; private SparseArray<View> footerViews; //头部类型开始位置,用于viewType private static int BASE_ITEM_TYPE_HEADER = 1000; //底部类型开始位置,用于viewType private static int BASE_ITEM_TYPE_FOOTER = 2000;
然后声明几个用来添加和移除View的方法
/** * 添加头部View * * @param view 头部View */ public void addHeaderView(View view) { if (headerViews.indexOfValue(view) < 0) { headerViews.put(BASE_ITEM_TYPE_HEADER++, view); notifyDataSetChanged(); } } /** * 添加底部View * * @param view 底部View */ public void addFooterView(View view) { if (footerViews.indexOfValue(view) < 0) { footerViews.put(BASE_ITEM_TYPE_FOOTER++, view); notifyDataSetChanged(); } } /** * 移除头部View * * @param view View */ public void removeHeaderView(View view) { int index = headerViews.indexOfValue(view); if (index > -1) { headerViews.removeAt(index); notifyDataSetChanged(); } } /** * 移除底部View * * @param view View */ public void removeFooterView(View view) { int index = footerViews.indexOfValue(view); if (index > -1) { footerViews.removeAt(index); notifyDataSetChanged(); } }
重点是 getItemViewType() 方法,如果索引值position指向的是顶部View或者底部View,则返回该View在 SparseArray< View >中的Key值,以该值作为View的 ItemViewType。如果索引指向的是中间的展示数据的子项,则调用adapter本身相同的方法
/** * 根据索引判断该位置的View类型 * 如果是头部,则返回该View在headerViews中的key * 如果是底部,则返回该View在footerViews中的key * * @param position 索引 * @return View类型 */ @Override public int getItemViewType(int position) { if (isHeaderPosition(position)) { return headerViews.keyAt(position); } if (isFooterPosition(position)) { position = position - headerViews.size() - adapter.getItemCount(); return footerViews.keyAt(position); } position = position - headerViews.size(); return adapter.getItemViewType(position); }
为不同的View指定了不同的ItemViewType后,则可以在onCreateViewHolder() 方法中返回不同的 ViewHolder 对象了
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (isHeaderViewType(viewType)) { return createHeaderFooterViewHolder(headerViews.get(viewType)); } if (isFooterViewType(viewType)) { return createHeaderFooterViewHolder(footerViews.get(viewType)); } return adapter.onCreateViewHolder(parent, viewType); }
总的方法定义如下所示:
/**
* 作者: 叶应是叶
* 时间: 2017/6/4
*/
public class WrapRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private RecyclerView.Adapter adapter;
private SparseArray<View> headerViews;
private SparseArray<View> footerViews;
//头部类型开始位置,用于viewType
private int BASE_ITEM_TYPE_HEADER = 1000;
//底部类型开始位置,用于viewType
private int BASE_ITEM_TYPE_FOOTER = 2000;
public WrapRecyclerViewAdapter(RecyclerView.Adapter adapter) {
this.adapter = adapter;
headerViews = new SparseArray<>();
footerViews = new SparseArray<>();
this.adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
notifyDataSetChanged();
}
});
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (isHeaderViewType(viewType)) {
return createHeaderFooterViewHolder(headerViews.get(viewType));
}
if (isFooterViewType(viewType)) {
return createHeaderFooterViewHolder(footerViews.get(viewType));
}
return adapter.onCreateViewHolder(parent, viewType);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (isHeaderPosition(position) || isFooterPosition(position)) {
return;
}
adapter.onBindViewHolder(holder, position - headerViews.size());
}
/**
* 获取列表总的条数(头部View个数+列表条数+底部View个数)
*
* @return 总的条数
*/
@Override
public int getItemCount() {
return adapter.getItemCount() + headerViews.size() + footerViews.size();
}
/**
* 获取不包含头部和底部View之后列表的条数
*
* @return 列表条数
*/
public int getDataItemCount() {
return adapter.getItemCount();
}
/**
* 根据索引判断该位置的View类型
* 如果是头部,则返回该View在headerViews中的key
* 如果是底部,则返回该View在footerViews中的key
*
* @param position 索引
* @return View类型
*/
@Override
public int getItemViewType(int position) {
if (isHeaderPosition(position)) {
return headerViews.keyAt(position);
}
if (isFooterPosition(position)) {
position = position - headerViews.size() - adapter.getItemCount();
return footerViews.keyAt(position);
}
position = position - headerViews.size();
return adapter.getItemViewType(position);
}
/**
* 创建头部View或底部View的ViewHolder
*
* @param view 头部View或底部View
* @return ViewHolder
*/
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) {
};
}
/**
* 判断是否是头部View
*
* @param key Key
* @return 是否是头部View
*/
private boolean isHeaderViewType(int key) {
return headerViews.indexOfKey(key) > -1;
}
/**
* 判断是否是底部View
*
* @param key Key
* @return 是否是底部View
*/
private boolean isFooterViewType(int key) {
return footerViews.indexOfKey(key) > -1;
}
/**
* 根据索引判断该位置的View是否是头部View
*
* @param position 索引
* @return 是否是头部View
*/
private boolean isHeaderPosition(int position) {
return (position > -1) && (position < headerViews.size());
}
/**
* 根据索引判断该位置的View是否是底部View
*
* @param position 索引
* @return 是否是底部View
*/
private boolean isFooterPosition(int position) {
return (position >= (headerViews.size() + adapter.getItemCount())) &&
(position < (headerViews.size() + adapter.getItemCount() + footerViews.size()));
}
/** * 添加头部View * * @param view 头部View */ public void addHeaderView(View view) { if (headerViews.indexOfValue(view) < 0) { headerViews.put(BASE_ITEM_TYPE_HEADER++, view); notifyDataSetChanged(); } } /** * 添加底部View * * @param view 底部View */ public void addFooterView(View view) { if (footerViews.indexOfValue(view) < 0) { footerViews.put(BASE_ITEM_TYPE_FOOTER++, view); notifyDataSetChanged(); } } /** * 移除头部View * * @param view View */ public void removeHeaderView(View view) { int index = headerViews.indexOfValue(view); if (index > -1) { headerViews.removeAt(index); notifyDataSetChanged(); } } /** * 移除底部View * * @param view View */ public void removeFooterView(View view) { int index = footerViews.indexOfValue(view); if (index > -1) { footerViews.removeAt(index); notifyDataSetChanged(); } }
}
三、自定义RecyclerView
继承 RecyclerView 实现 WrapRecyclerView 子类首先需要声明两个变量
/** * 用来指向传入的 Adapter 或来构造 WrapRecyclerViewAdapter */ private WrapRecyclerViewAdapter wrapRecyclerViewAdapter; /** * 用来指向传入的Adapter */ private Adapter mRecyclerAdapter;
仿照ListView的思路来重写 setAdapter() 方法,构建一个 WrapRecyclerViewAdapter 对象作为实际的Adapter。
当中,需要注意的是,如果传入的 recyclerAdapter 是直接继承于 WrapViewRecycleAdapter 的话,则直接强转类型就可以了,否则的话需要再来根据 recyclerAdapter 构造一个 WrapViewRecycleAdapter 对象
如果 wrapRecyclerViewAdapter 是通过强转得来的,则当 mRecyclerAdapter 数据刷新时,wrapRecyclerViewAdapter 自然也会做出相应的变化,因为两者指向的是同一个对象。
如果 wrapRecyclerViewAdapter 是用new关键字重新声明的,则需要在为 mRecyclerAdapter 注册一个观察者对象,在 mRecyclerAdapter 数据刷新时同时通知 wrapRecyclerViewAdapter 也进行数据刷新。
@Override public void setAdapter(Adapter recyclerAdapter) { if (mRecyclerAdapter != null) { mRecyclerAdapter.unregisterAdapterDataObserver(adapterDataObserver); mRecyclerAdapter = null; } mRecyclerAdapter = recyclerAdapter; // 如果传入的 recyclerAdapter 是直接继承于 WrapViewRecycleAdapter 的话,则直接强转类型 // 否则的话再来根据 recyclerAdapter 构造一个 WrapViewRecycleAdapter if (mRecyclerAdapter instanceof WrapRecyclerViewAdapter) { wrapRecyclerViewAdapter = (WrapRecyclerViewAdapter) mRecyclerAdapter; } else { // 注册观察者对象 mRecyclerAdapter.registerAdapterDataObserver(adapterDataObserver); wrapRecyclerViewAdapter = new WrapRecyclerViewAdapter(mRecyclerAdapter); } super.setAdapter(wrapRecyclerViewAdapter); }
总的代码如下所示:
/** * 作者: 叶应是叶 * 时间: 2017/6/4 * 描述: 可以带头部View与尾部View的RecyclerView */ public class WrapRecyclerView extends RecyclerView { /** * 用来指向传入的 Adapter 或来构造 WrapRecyclerViewAdapter */ private WrapRecyclerViewAdapter wrapRecyclerViewAdapter; /** * 用来指向传入的Adapter */ private Adapter mRecyclerAdapter; public WrapRecyclerView(Context context) { super(context); } public WrapRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); } public WrapRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public void setAdapter(Adapter recyclerAdapter) { if (mRecyclerAdapter != null) { mRecyclerAdapter.unregisterAdapterDataObserver(adapterDataObserver); mRecyclerAdapter = null; } mRecyclerAdapter = recyclerAdapter; // 如果传入的 recyclerAdapter 是直接继承于 WrapViewRecycleAdapter 的话,则直接强转类型 // 否则的话再来根据 recyclerAdapter 构造一个 WrapViewRecycleAdapter if (mRecyclerAdapter instanceof WrapRecyclerViewAdapter) { wrapRecyclerViewAdapter = (WrapRecyclerViewAdapter) mRecyclerAdapter; } else { // 注册观察者对象 mRecyclerAdapter.registerAdapterDataObserver(adapterDataObserver); wrapRecyclerViewAdapter = new WrapRecyclerViewAdapter(mRecyclerAdapter); } super.setAdapter(wrapRecyclerViewAdapter); } /** * AdapterDataObserver是RecyclerView内部的一个抽象类 * 用来作为观察者监听数据变化 */ private AdapterDataObserver adapterDataObserver = new AdapterDataObserver() { @Override public void onChanged() { if (wrapRecyclerViewAdapter != mRecyclerAdapter) { wrapRecyclerViewAdapter.notifyDataSetChanged(); } } @Override public void onItemRangeRemoved(int positionStart, int itemCount) { if (wrapRecyclerViewAdapter != mRecyclerAdapter) { wrapRecyclerViewAdapter.notifyItemRemoved(positionStart); } } @Override public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) { if (wrapRecyclerViewAdapter != mRecyclerAdapter) { wrapRecyclerViewAdapter.notifyItemMoved(fromPosition, toPosition); } } @Override public void onItemRangeChanged(int positionStart, int itemCount) { if (wrapRecyclerViewAdapter != mRecyclerAdapter) { wrapRecyclerViewAdapter.notifyItemChanged(positionStart); } } @Override public void onItemRangeChanged(int positionStart, int itemCount, Object payload) { if (wrapRecyclerViewAdapter != mRecyclerAdapter) { wrapRecyclerViewAdapter.notifyItemChanged(positionStart, payload); } } @Override public void onItemRangeInserted(int positionStart, int itemCount) { if (wrapRecyclerViewAdapter != mRecyclerAdapter) { wrapRecyclerViewAdapter.notifyItemInserted(positionStart); } } }; /** * 添加头部View * * @param view View */ public void addHeaderView(View view) { if (wrapRecyclerViewAdapter != null) { wrapRecyclerViewAdapter.addHeaderView(view); } else { throw new RuntimeException("WrapRecyclerViewAdapter == null"); } } /** * 添加底部View * * @param view View */ public void addFooterView(View view) { if (wrapRecyclerViewAdapter != null) { wrapRecyclerViewAdapter.addFooterView(view); } else { throw new RuntimeException("WrapRecyclerViewAdapter == null"); } } /** * 移除头部View * * @param view View */ public void removeHeaderView(View view) { if (wrapRecyclerViewAdapter != null) { wrapRecyclerViewAdapter.removeHeaderView(view); } else { throw new RuntimeException("WrapRecyclerViewAdapter == null"); } } /** * 移除底部View * * @param view View */ public void removeFooterView(View view) { if (wrapRecyclerViewAdapter != null) { wrapRecyclerViewAdapter.removeFooterView(view); } else { throw new RuntimeException("WrapRecyclerViewAdapter == null"); } } }
四、实际使用
以我上一篇文章:解析RecyclerView(1)——带点击事件监听的通用Adapter 使用到的 MyCommonRecyclerAdapter 类作为最原始的Adapter在布局文件中声明的RecyclerView就要使用自定义的 WrapRecyclerView 了,再增加几个按钮用于进行RecyclerView展示的数据,顶部View和底部View的增添删除操作。
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.czy.demo.RecyclerView.Wrap.WrapRecyclerActivity"> <Button android:id="@+id/btn_addData" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="增添数据" /> <Button android:id="@+id/btn_deleteData" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/btn_addData" android:text="删除数据" /> <Button android:id="@+id/btn_addHeaderView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/btn_deleteData" android:text="增加头部View" /> <Button android:id="@+id/btn_deleteHeaderView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/btn_addHeaderView" android:text="删除头部View" /> <Button android:id="@+id/btn_addFooterView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/btn_deleteHeaderView" android:text="增加底部View" /> <Button android:id="@+id/btn_deleteFooterView" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/btn_addFooterView" android:text="删除底部View" /> <com.czy.common.RecyclerView.Wrap.WrapRecyclerView android:id="@+id/wrv_dataList" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/btn_deleteFooterView" /> </RelativeLayout>
Activity总的代码如下所示,加载的顶部View布局文件 R.layout.header_view 只包含含一个 ImageView 控件,底部View布局文件 R.layout.footer _view 只包含一个 TextView 控件
public class WrapRecyclerActivity extends AppCompatActivity implements CommonRecyclerHolder.onClickCommonListener, View.OnClickListener { private List<Data> dataList; private List<View> headerViewList; private List<View> footerViewList; private WrapRecyclerView wrv_dataList; private MyCommonRecyclerAdapter myCommonRecyclerAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wrap_recycler); initData(); wrv_dataList = (WrapRecyclerView) findViewById(R.id.wrv_dataList); wrv_dataList.setLayoutManager(new LinearLayoutManager(this)); myCommonRecyclerAdapter = new MyCommonRecyclerAdapter(this, dataList, R.layout.item, this); wrv_dataList.setAdapter(myCommonRecyclerAdapter); View headerView1 = getLayoutInflater().inflate(R.layout.header_view, wrv_dataList, false); View headerView2 = getLayoutInflater().inflate(R.layout.header_view, wrv_dataList, false); View footerView1 = getLayoutInflater().inflate(R.layout.footer_view, wrv_dataList, false); View footerView2 = getLayoutInflater().inflate(R.layout.footer_view, wrv_dataList, false); wrv_dataList.addHeaderView(headerView1); wrv_dataList.addHeaderView(headerView2); wrv_dataList.addFooterView(footerView1); wrv_dataList.addFooterView(footerView2); headerViewList.add(headerView1); headerViewList.add(headerView2); footerViewList.add(footerView1); footerViewList.add(footerView2); findViewById(R.id.btn_addData).setOnClickListener(this); findViewById(R.id.btn_deleteData).setOnClickListener(this); findViewById(R.id.btn_addHeaderView).setOnClickListener(this); findViewById(R.id.btn_deleteHeaderView).setOnClickListener(this); findViewById(R.id.btn_addFooterView).setOnClickListener(this); findViewById(R.id.btn_deleteFooterView).setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_addData: Data data = new Data(R.mipmap.ic_launcher, "Hi"); dataList.add(data); myCommonRecyclerAdapter.notifyDataSetChanged(); break; case R.id.btn_deleteData: if (dataList.size() > 0) { dataList.remove(0); } myCommonRecyclerAdapter.notifyDataSetChanged(); break; case R.id.btn_addHeaderView: View headerView = getLayoutInflater().inflate(R.layout.header_view, wrv_dataList, false); headerViewList.add(headerView); wrv_dataList.addHeaderView(headerView); break; case R.id.btn_deleteHeaderView: if (headerViewList.size() > 0) { wrv_dataList.removeHeaderView(headerViewList.get(0)); headerViewList.remove(0); } break; case R.id.btn_addFooterView: View footerView = getLayoutInflater().inflate(R.layout.footer_view, wrv_dataList, false); footerViewList.add(footerView); wrv_dataList.addFooterView(footerView); break; case R.id.btn_deleteFooterView: if (footerViewList.size() > 0) { wrv_dataList.removeFooterView(footerViewList.get(0)); footerViewList.remove(0); } break; } } private void initData() { dataList = new ArrayList<>(); headerViewList = new ArrayList<>(); footerViewList = new ArrayList<>(); for (int i = 0; i < 50; i++) { Data data = new Data(R.mipmap.ic_launcher_round, "Hi:" + i); dataList.add(data); } } @Override public void onClick(int position) { Toast.makeText(this, "点击:" + position, Toast.LENGTH_SHORT).show(); } @Override public void onLongClick(int position) { Toast.makeText(this, "长按:" + position, Toast.LENGTH_SHORT).show(); } }
运行效果:
这里提供代码下载:解析RecyclerView(2)——带顶部View和底部View的RecyclerView
相关文章推荐
- Android 解析RecyclerView(3)——以更简单的方法实现带顶部View和底部View的RecyclerView
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android开发--------------WebView(二)之WebView的滑动底部顶部监听,加载进度条等设置
- 【Android开源项目解析】RecyclerView侧滑删除粒子效果实现——初探Android开源粒子库 Leonids
- Android RecyclerView 使用完全解析 体验艺术般的控件
- RecyclerView 滑动检测 (上滑 up)(下滑 down)(顶部 top)(底部 bottom)
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView 使用完全解析
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView 使用解析
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView多个Item布局的实现(可实现头部底部)
- android里,addContentView()动态增加view控件,并实现控件的顶部,中间,底部布局
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView 使用完全解析
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android RecyclerView 使用完全解析 体验艺术般的控件