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--添加头部和底部的更多相关文章

  1. RecyclerView添加头部和底部视图的实现

    ListView是有addHeaderView和 addFooterView两个方法的. 但是作为官方推荐的ListView的升级版RecyclerView缺无法实现这两个方法. 那么如果使用Recy ...

  2. RecyclerView添加头部和底部视图的实现方法

    引用-- http://www.zhimengzhe.com/Androidkaifa/15072.html 在天下货crm----签到---签到记录中有使用

  3. 可添加头部尾部RecyclerView,很帅哦~

    WrapRecyclerView 是一个可以添加头部和尾部的RecyclerView,并且提供了一个 WrapAdapter, 它可以让你轻松为 RecyclerView 添加头部和尾部.   示例中 ...

  4. Android 5.X新特性之为RecyclerView添加HeaderView和FooterView

    上一节我们讲到了 Android 5.X新特性之RecyclerView基本解析及无限复用 相信大家也应该熟悉了RecyclerView的基本使用,这一节我们来学习下,为RecyclerView添加H ...

  5. Android RecyclerView添加Header头部

     Android RecyclerView添加Header头部 Android RecyclerView不像以前的ListView那样直接添加头部,如果要给RecyclerView增加头部,则需要 ...

  6. Android 5.X新特性之为RecyclerView添加下拉刷新和上拉加载及SwipeRefreshLayout实现原理

    RecyclerView已经写过两篇文章了,分别是Android 5.X新特性之RecyclerView基本解析及无限复用 和 Android 5.X新特性之为RecyclerView添加Header ...

  7. RecyclerView添加Header的正确方式

    原文链接:http://blog.csdn.net/qibin0506/article/details/49716795 看了一下博客目录,已经有好几篇博客是关于RecyclerView的,不过对于这 ...

  8. HTML5 开发APP(头部和底部选项卡)

    我们开发app有一定固定的样式,比如头部和底部选项卡部分就是公共部分就比如我在做的app进来的主页面就像图片显示的那样 我们该怎么实现呢,实现我们应该建一个主页面index.html,然后建五个子页面 ...

  9. ionic-CSS:ionic 头部与底部

    ylbtech-ionic-CSS:ionic 头部与底部 1.返回顶部 1. ionic 头部与底部 Header(头部) Header是固定在屏幕顶部的组件,可以包如标题和左右的功能按钮. ion ...

随机推荐

  1. FactoryMethod工厂方法模式(创建型模式)

    1.工厂方法模式解决的问题 现在有一个抽象的游戏设施建造系统,负责构建一个现代风格和古典风格的房屋和道路. 前提:抽象变化较慢,实现变化较快(不稳定) 整个抽象的游戏设施建造系统相对变化较慢,本例中只 ...

  2. Selenium之元素定位

    1.查看页面元素:ID.class.type.name等. 2.通过webdriver的方法定位: find_element_by_name()  find_element_by_id()  find ...

  3. Android开发艺术探索学习笔记(四)

    第四章 View的工作原理 4.1初识ViewRoot和DecorView ViewRoot是连接WindowManager和DecorView的纽带,View的三大流程均是通过ViewRoot来完成 ...

  4. php -- 数据库信息

    ----- 023-dbinfo.php ----- <!DOCTYPE html> <html> <head> <meta http-equiv=" ...

  5. 超漂亮的CSS3按钮制作教程分享

    要求 必备知识 基本了解CSS语法,初步了解CSS3语法知识. 开发环境 Adobe Dreamweaver CS6/Chrome浏览器 演示地址 演示地址 利用CSS3属性制作按钮,我们只需要用到G ...

  6. Vue笔记:webpack项目vue启动流程

    VUE启动流程 1. package.json 在执行npm run dev的时候,会在当前目录中寻找 package.json 文件, 有点类似 Maven 的 pom.xml 文件,包含项目的名称 ...

  7. zmq 三种模型的python实现

    1.Request-Reply模式: 客户端在请求后,服务端必须回响应 server: #!/usr/bin/python #-*-coding:utf-8-*- import time import ...

  8. tsung压力测试——Tsung测试统计报告说明【转】

    1.主要统计信息 Tsung统计数据是平均每十秒重置一次,所以这里的响应时间(连接.请求.页面.会话)是指每十秒的平均响应时间: connect: 表示 每个连接持续时间: Hightest 10se ...

  9. 【胡思乱想】命令模式 与 Thread Runnable

    实现线程任务有两种方式,一种是创建Runnable/Callable对象,传递给Thread.另一种是子类重写Thread的run方法. 我觉得前一种像极了命令模式,或者说应该是命令模式的一个使用案例 ...

  10. 百度全站 https FAQ:技术宅告诉你如何搜索更安全

    百度从 14 年开始对外开放了 https 的访问,并于 3 月初正式对全网用户进行了 https 跳转. 你也许会问,切换就切换呗,和我有啥关系?我平常用百度还不是照常顺顺当当的,没感觉到什么切换. ...