RecyclerView--添加头部和底部
1.先构建WrapRecyclerAdapter
/**
* Description: 可以添加头部和底部的Adapter
*/
public class WrapRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final static String TAG = "WrapRecyclerAdapter"; /**
* SparseArrays map integers to Objects. Unlike a normal array of Objects,
* there can be gaps in the indices. It is intended to be more memory efficient
* than using a HashMap to map Integers to Objects, both because it avoids
* auto-boxing keys and its data structure doesn't rely on an extra entry object
* for each mapping.
*
* SparseArray是一个<int , Object>的HashMap 比HashMap更高效
*/
private SparseArray<View> mHeaderViews;
private SparseArray<View> mFooterViews; // 基本的头部类型开始位置 用于viewType
private static int BASE_ITEM_TYPE_HEADER = ;
// 基本的底部类型开始位置 用于viewType
private static int BASE_ITEM_TYPE_FOOTER = ; /**
* 数据列表的Adapter
*/
private RecyclerView.Adapter mAdapter; public WrapRecyclerAdapter(RecyclerView.Adapter adapter) {
this.mAdapter = adapter;
mHeaderViews = new SparseArray<>();
mFooterViews = new SparseArray<>();
} @Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { // 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 >= ;
} /**
* 创建头部或者底部的ViewHolder
*/
private RecyclerView.ViewHolder createHeaderFooterViewHolder(View view) {
return new RecyclerView.ViewHolder(view) { };
} /**
* 是不是头部类型
*/
private boolean isHeaderViewType(int viewType) {
int position = mHeaderViews.indexOfKey(viewType);
return position >= ;
} @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
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(v, adapterPosition);
}
});
}
if (mLongClickListener != null) {
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return mLongClickListener.onLongClick(v, adapterPosition);
}
});
}
} @Override
public int getItemViewType(int position) {
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);
} /**
* 是不是底部位置
*/
private boolean isFooterPosition(int position) {
return position >= (mHeaderViews.size() + mAdapter.getItemCount());
} /**
* 是不是头部位置
*/
private boolean isHeaderPosition(int position) {
return position < mHeaderViews.size();
} @Override
public int getItemCount() {
// 条数三者相加 = 底部条数 + 头部条数 + Adapter的条数
return mAdapter.getItemCount() + mHeaderViews.size() + mFooterViews.size();
} /**
* 获取列表的Adapter
*/
private RecyclerView.Adapter getAdapter() {
return mAdapter;
} // 添加头部
public void addHeaderView(View view) {
int position = mHeaderViews.indexOfValue(view);
if (position < ) {
mHeaderViews.put(BASE_ITEM_TYPE_HEADER++, view);
}
notifyDataSetChanged();
} // 添加底部
public void addFooterView(View view) {
int position = mFooterViews.indexOfValue(view);
if (position < ) {
mFooterViews.put(BASE_ITEM_TYPE_FOOTER++, view);
}
notifyDataSetChanged();
} // 移除头部
public void removeHeaderView(View view) {
int index = mHeaderViews.indexOfValue(view);
if (index < ) return;
mHeaderViews.removeAt(index);
notifyDataSetChanged();
} // 移除底部
public void removeFooterView(View view) {
int index = mFooterViews.indexOfValue(view);
if (index < ) return;
mFooterViews.removeAt(index);
notifyDataSetChanged();
} /**
* 解决GridLayoutManager添加头部和底部不占用一行的问题
*
* @param recycler
* @version 1.0
*/
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() : ;
}
});
}
} /***************
* 给条目设置点击和长按事件
*********************/
public OnItemClickListener mItemClickListener;
public OnLongClickListener mLongClickListener; public void setOnItemClickListener(OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener;
} public void setOnLongClickListener(OnLongClickListener longClickListener) {
this.mLongClickListener = longClickListener;
}
}
2.构建WrapRecyclerView
我们最好还是模仿ListView的结构搞就搞到西,自定义一个WrapRecyclerView,可以添加删除头部和底部View,这个就比较简单
/**
* Description: 可以添加头部和底部的RecyclerView
*/
public class WrapRecyclerView extends RecyclerView {
// 包裹了一层的头部底部Adapter
private WrapRecyclerAdapter mWrapRecyclerAdapter;
// 这个是列表数据的Adapter
private Adapter mAdapter; // 增加一些通用功能
// 空列表数据应该显示的空View
// 正在加载数据页面,也就是正在获取后台接口页面
private View mEmptyView, mLoadingView; 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); // 解决GridLayout添加头部和底部也要占据一行
mWrapRecyclerAdapter.adjustSpanSize(this); // 加载数据页面
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();
}
}; /**
* 添加一个空列表数据页面
*/
public void addEmptyView(View emptyView) {
this.mEmptyView = emptyView;
} /**
* 添加一个正在加载数据的页面
*/
public void addLoadingView(View loadingView) {
this.mLoadingView = loadingView;
mLoadingView.setVisibility(View.VISIBLE);
} /**
* Adapter数据改变的方法
*/
private void dataChanged() {
if (mAdapter.getItemCount() == ) {
// 没有数据
if (mEmptyView != null) {
mEmptyView.setVisibility(VISIBLE);
}
} else {
// 没有数据
if (mEmptyView != null) {
mEmptyView.setVisibility(GONE);
}
}
} /***************
* 给条目设置点击和长按事件
*********************/
public com.zzw.framelibray.recyclerview.adapter.OnItemClickListener mItemClickListener;
public com.zzw.framelibray.recyclerview.adapter.OnLongClickListener mLongClickListener; public void setOnItemClickListener(com.zzw.framelibray.recyclerview.adapter.OnItemClickListener itemClickListener) {
this.mItemClickListener = itemClickListener; if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.setOnItemClickListener(mItemClickListener);
}
} public void setOnLongClickListener(com.zzw.framelibray.recyclerview.adapter.OnLongClickListener longClickListener) {
this.mLongClickListener = longClickListener; if (mWrapRecyclerAdapter != null) {
mWrapRecyclerAdapter.setOnLongClickListener(mLongClickListener);
}
}
}
3.使用:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"> <com.zzw.framelibray.recyclerview.view.WrapRecyclerView
android:id="@+id/wrap_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" /> </LinearLayout>
Activity
public class HeaderFooterActivity extends AppCompatActivity implements OnItemClickListener {
private WrapRecyclerView mRecyclerView;
private List<People> mData;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler_view);
mRecyclerView = (WrapRecyclerView) findViewById(R.id.wrap_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mData =new ArrayList<>();
mData.add(new People());
mData.add(new People());
mData.add(new People());
PeopleListAdapter listAdapter = new PeopleListAdapter(this, mData);
// 添加头部和底部 需要 包裹Adapter,才能添加头部和底部
WrapRecyclerAdapter wrapRecyclerAdapter = new WrapRecyclerAdapter(listAdapter);
mRecyclerView.setAdapter(wrapRecyclerAdapter);
wrapRecyclerAdapter.setOnItemClickListener(this);
// 添加头部和底部
wrapRecyclerAdapter.addHeaderView(LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false));
wrapRecyclerAdapter.addFooterView(LayoutInflater.from(this).inflate(R.layout.layout_header,mRecyclerView,false));
}
@Override
public void onItemClick(View view, int position) {
Toast.makeText(this, "" + mData.get(position).name, Toast.LENGTH_SHORT).show();
}
class PeopleListAdapter extends CommonRecyclerAdapter<People> {
public PeopleListAdapter(Context context, List<People> datas) {
super(context, datas, R.layout.channel_list_item);
}
@Override
public void convert(ViewHolder holder, People item, int position) {
holder.setText(R.id.action_btn, item.name+position);
}
}
class People{
String name="王伟:";
}
}
RecyclerView--添加头部和底部的更多相关文章
- RecyclerView添加头部和底部视图的实现
ListView是有addHeaderView和 addFooterView两个方法的. 但是作为官方推荐的ListView的升级版RecyclerView缺无法实现这两个方法. 那么如果使用Recy ...
- RecyclerView添加头部和底部视图的实现方法
引用-- http://www.zhimengzhe.com/Androidkaifa/15072.html 在天下货crm----签到---签到记录中有使用
- 可添加头部尾部RecyclerView,很帅哦~
WrapRecyclerView 是一个可以添加头部和尾部的RecyclerView,并且提供了一个 WrapAdapter, 它可以让你轻松为 RecyclerView 添加头部和尾部. 示例中 ...
- Android 5.X新特性之为RecyclerView添加HeaderView和FooterView
上一节我们讲到了 Android 5.X新特性之RecyclerView基本解析及无限复用 相信大家也应该熟悉了RecyclerView的基本使用,这一节我们来学习下,为RecyclerView添加H ...
- Android RecyclerView添加Header头部
Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...
- Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理
RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...
- RecyclerView添加Header的正确方式
原文链接:http://blog.csdn.net/qibin0506/article/details/49716795 看了一下博客目录,已经有好几篇博客是关于RecyclerView的,不过对于这 ...
- HTML5 开发APP(头部和底部选项卡)
我们开发app有一定固定的样式,比如头部和底部选项卡部分就是公共部分就比如我在做的app进来的主页面就像图片显示的那样 我们该怎么实现呢,实现我们应该建一个主页面index.html,然后建五个子页面 ...
- ionic-CSS:ionic 头部与底部
ylbtech-ionic-CSS:ionic 头部与底部 1.返回顶部 1. ionic 头部与底部 Header(头部) Header是固定在屏幕顶部的组件,可以包如标题和左右的功能按钮. ion ...
随机推荐
- 剑指offer八之跳台阶
一.题目 一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法. 二.思路 a.如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法 ...
- Hadoop 2.4.1+HBase 0.98.6.1 分布式安装
参考:http://blog.csdn.net/wind520/article/details/39856353
- atexit()使用
mian()主函数执行完毕后,是否可能会再执行一段代码?如果需要加入一段代码在mian退出后执行的代码,可以使用atexit()函数注册一个函数,代码如下: #include <iostream ...
- Java 多线程学习笔记:生产者消费者问题
前言:最近在学习Java多线程,看到ImportNew网上有网友翻译的一篇文章<阻塞队列实现生产者消费者模式>.在文中,使用的是Java的concurrent包中的阻塞队列来实现.在看完后 ...
- SwitchHosts—hosts管理工具
SwitchHosts是一个管理.快速切换Hosts小工具,开源软件,一键切换Hosts配置,非常实用,高效.开发Web过程成,部署有多套环境,网址域名都相同,部署在不同的服务器上,有开发环境.测试环 ...
- wordpress给文章添加缩略图
百度是个好东西,翻了半个小时的文章,终于把这个问题解决了. 一个问题的解决方法很多,但要找到一个自己理解的方法,缺比较难找,不管怎样,多动手,可能弄着弄着就会了. 教程开始: 1.先去后台管理安装Ea ...
- mongo学习使用记录2 spring data
spring data mongo 打印mongo NoSql语句 log4j.properties log4j.rootLogger=INFO, stdout log4j.logger.org.sp ...
- mongodb备份策略
概述 数据库的备份非常非常非常重要!!!否则出问题连哭的机会有没有(欲哭无泪)今天主要是做一个mongodb的数据库备份. 1.关于备份 备份其实很简单,这里选择的是对mongodb中的某个库进行全备 ...
- AJAX unsupported media type 415错误处理
一.问题 在使用angular做请求拦截时,因为依赖循环的问题,在请求拦截中改为使用ajax来发起请求拿到我想要的数据,结果出现了415 Unsupported Media Type错误,由于很久没使 ...
- 因为错误关闭Selinux导致CentOS7启动失败(进度条卡死,图形界面加载卡死)
我在CentOS7上安装oracle,非常麻烦,搞半天终于安装完毕,当天我没有发现任何问题,第二天上班打开虚拟机CentOS7就进不去了. 我想起来之前关闭了Selinux,把系统名称改成了redha ...