RecyclerView添加Header的正确方式

article/2025/11/10 5:17:10

看了一下博客目录,已经有好几篇博客是关于RecyclerView的,不过对于这么一款强大的控件,我还是要再写一篇博客来学习一下,这篇博客的主题是《为RecyclerView添加header》,当然在看完这篇博客后,相信添加Footer你也应该能够学会。话说在这么多新控件中为何RecyclerView备受开发者的喜爱?这还是因为在Android发展到今天基本上还没有像RecyclerView这么灵活的一个玩意,鉴于他的灵活以及强大,很多人(包括我)已经开始抛弃ListViewGridView转为RecyclerView了,再使用过RecyclerView和被善变的需求折磨后,我相信会有越来越多的人转到RecyclerView的使用上。

问题

好了,废话不多说了,这篇博客我们要解决的问题有:

  1. 如何为RecyclerView添加Header
  2. 如何让Header适配各种LayoutManager
  3. 在有Header的情况下,我们的分割线该怎么画
  4. 作为一个懒惰的程序员,如何将这些做到最简便

如果为RecyclerView添加Header

大家在使用ListView的时候可以很轻松的添加headers, 但是不知道大家发现没有,RecyclerView和各种LayoutManager都没有哪个方法是为添加header而设立的,这个时候我们就开始思考如何为RecyclerView添加header了。 这里我们的解决方案和网上你能搜到的大多数方案一样,是通过控制AdapteritemType来设置的,思路就是根据不同的itemType去加载不同的布局

/*** Created by qibin on 2015/11/5.*/
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {public static final int TYPE_HEADER = 0;public static final int TYPE_NORMAL = 1;private ArrayList<String> mDatas = new ArrayList<>();private View mHeaderView;private OnItemClickListener mListener;public void setOnItemClickListener(OnItemClickListener li) {mListener = li;}public void setHeaderView(View headerView) {mHeaderView = headerView;notifyItemInserted(0);}public View getHeaderView() {return mHeaderView;}public void addDatas(ArrayList<String> datas) {mDatas.addAll(datas);notifyDataSetChanged();}@Overridepublic int getItemViewType(int position) {if(mHeaderView == null) return TYPE_NORMAL;if(position == 0) return TYPE_HEADER;return TYPE_NORMAL;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {if(mHeaderView != null && viewType == TYPE_HEADER) return new Holder(mHeaderView);View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);return new Holder(layout);}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {if(getItemViewType(position) == TYPE_HEADER) return;final int pos = getRealPosition(viewHolder);final String data = mDatas.get(pos);if(viewHolder instanceof Holder) {((Holder) viewHolder).text.setText(data);if(mListener == null) return;viewHolder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mListener.onItemClick(pos, data);}});}}public int getRealPosition(RecyclerView.ViewHolder holder) {int position = holder.getLayoutPosition();return mHeaderView == null ? position : position - 1;}@Overridepublic int getItemCount() {return mHeaderView == null ? mDatas.size() : mDatas.size() + 1;}class Holder extends RecyclerView.ViewHolder {TextView text;public Holder(View itemView) {super(itemView);if(itemView == mHeaderView) return;text = (TextView) itemView.findViewById(R.id.text);}}interface OnItemClickListener {void onItemClick(int position, String data);}
}

这里我们重写了getItemViewType方法,并根据位置来返回不同的type,这个type是我们预先商定好的常量,接在onCreateViewHolder方法中来判断itemType,如果是header,则返回我们设置的headerView,否则正常加载item布局,相信大家对于上面的代码不会有任何疑问,接下来我们就在Activity中用一下试试看,

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mRecyclerView = (RecyclerView) findViewById(R.id.list);mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);mRecyclerView.setLayoutManager(mLayoutManager);mRecyclerView.setItemAnimator(new DefaultItemAnimator());mAdapter = new MyAdapter();mRecyclerView.setAdapter(mAdapter);mAdapter.addDatas(generateData());setHeader(mRecyclerView);mAdapter.setOnItemClickListener(new MyAdapter.OnItemClickListener() {@Overridepublic void onItemClick(int position, String data) {Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();}});
}private void setHeader(RecyclerView view) {View header = LayoutInflater.from(this).inflate(R.layout.header, view, false);mAdapter.setHeaderView(header);
}

这里LayoutManager我们使用了LinearLayoutManager,并且给Adapter设置了一个header,运行一下
看看效果:

恩,还不错,item的点击事件也很完美,那接下来,我们将LayoutManager换成GridLayoutManager看看咋样。

为GridLayoutManager添加header

//  mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mLayoutManager = new GridLayoutManager(this, 2);

哎哟,我的小心脏啊,快受不了了,这是什么玩意,我们的header竟然作为一个cell出现在了界面上,这完全不是我们想要的效果啊! 冷静下来想想,肯定会有解决方法的吧。这时候我们就该引入一个不太常用的方法了:

gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {return getItemViewType(position) == TYPE_HEADER? gridManager.getSpanCount() : 1;}
});

我们解释一下这段代码,首先我们设置了一个SpanSizeLookup,这个类是一个抽象类,而且仅有一个抽象方法getSpanSize,这个方法的返回值决定了我们每个position上的item占据的单元格个数,而我们这段代码综合上面为GridLayoutManager设置的每行的个数来解释的话,
就是当前位置是header的位置,那么该item占据2个单元格,正常情况下占据1个单元格。那这段代码放哪呢? 为了以后的封装,我们还是在Adapter中找方法放吧。
我们在Adapter中再重写一个方法onAttachedToRecyclerView

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {super.onAttachedToRecyclerView(recyclerView);RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();if(manager instanceof GridLayoutManager) {final GridLayoutManager gridManager = ((GridLayoutManager) manager);gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {return getItemViewType(position) == TYPE_HEADER? gridManager.getSpanCount() : 1;}});}
}

这个时候我们再来看一下效果,

恩,这次达到我们的要求了,不过对于StaggeredGridLayoutManager我们还没做处理,而且我们还发现StaggeredGridLayoutManager中并没有像GridLayoutManager中这样的方法,我们还需要单独为StaggeredGridLayoutManager单独处理一下。

为StaggeredGridLayoutManager添加header

我们继续重写Adapter中另外一个方法。

@Override
public void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {super.onViewAttachedToWindow(holder);ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();if(lp != null&& lp instanceof StaggeredGridLayoutManager.LayoutParams) {StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;p.setFullSpan(holder.getLayoutPosition() == 0);}
}

这里的处理方式是用通过LayoutParams,而且这里更简单,StaggeredGridLayoutManager.LayoutParams为我们提供了一个setFullSpan方法来设置占领全部空间,好开心,看一下StaggeredGridLayoutManager的效果,

啊, 怎么和上面的效果一样? 很简单嘛,我们的item都是等高的。

处理分隔符

这是我们开开心心的继续写代码,并且为我们的item添加了分隔符,分隔符我还是用的翔哥写的那个,毕竟翔哥写的太好了,而且我们没有必要重复造轮子,不过这时候问题出现了,相信你也肯定能猜到应该会出现问题了,因为不管我们怎么处理,header对于RecyclerView来说还是一个普普通通的item,这时候我们添加分割线,肯定也会对header产生影响,那下面,我们再来对翔哥的分割线改造一下吧。

public class GridItemDecoration extends RecyclerView.ItemDecoration {private static final int[] ATTRS = new int[]{android.R.attr.listDivider};private Drawable mDivider;private boolean hasHeader;public GridItemDecoration(Context context) {final TypedArray a = context.obtainStyledAttributes(ATTRS);mDivider = a.getDrawable(0);a.recycle();}public GridItemDecoration(Context context, boolean header) {this(context);hasHeader = header;}...@Overridepublic void getItemOffsets(Rect outRect, View view,RecyclerView parent, RecyclerView.State state) {int position = parent.getChildAdapterPosition(view);int spanCount = getSpanCount(parent);int childCount = parent.getAdapter().getItemCount();int pos = position;if(hasHeader) {if(position == 0) {outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());return;} else {pos = position - 1;}}if (isLastColum(parent, pos, spanCount, childCount)) {outRect.set(0, 0, mDivider.getIntrinsicWidth(), mDivider.getIntrinsicHeight());} else {outRect.set(0, 0, mDivider.getIntrinsicWidth(),mDivider.getIntrinsicHeight());}}
}

改造的地方是获取偏移量的方法我们换了一个,因为原来的那个已经过时了,而且,这里我们还加了一个boolean类型的hasHeader变量来表示是不是有header,如果hasHeader并且position为0,那么我们仅仅绘制底部的分割线,其他的地方不绘制,在有header的情况下,我们还需要将position减1,因为我们认为的第1个item其实是第2个。这个时候我们再来看看有分割线的效果。

看来我们的想法是对的,header部分除了底部有一个分割线外,并没有其他的分割线,这也完全符合我们的需求。

封装

这下好了,基本上完美的处理好了,可是难道我们对于不同的Adapter都需要写那么多代码吗? 对于一个懒程序员来说,这肯定是一个可怕的事情,所以,我们还需要对我们的Adapter进行封装,目的就是可以轻轻松松的写代码,

/*** Created by qibin on 2015/11/5.*/
public abstract class BaseRecyclerAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {public static final int TYPE_HEADER = 0;public static final int TYPE_NORMAL = 1;private ArrayList<T> mDatas = new ArrayList<>();private View mHeaderView;private OnItemClickListener mListener;public void setOnItemClickListener(OnItemClickListener li) {mListener = li;}public void setHeaderView(View headerView) {mHeaderView = headerView;notifyItemInserted(0);}public View getHeaderView() {return mHeaderView;}public void addDatas(ArrayList<T> datas) {mDatas.addAll(datas);notifyDataSetChanged();}@Overridepublic int getItemViewType(int position) {if(mHeaderView == null) return TYPE_NORMAL;if(position == 0) return TYPE_HEADER;return TYPE_NORMAL;}@Overridepublic RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {if(mHeaderView != null && viewType == TYPE_HEADER) return new Holder(mHeaderView);return onCreate(parent, viewType);}@Overridepublic void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {if(getItemViewType(position) == TYPE_HEADER) return;final int pos = getRealPosition(viewHolder);final T data = mDatas.get(pos);onBind(viewHolder, pos, data);if(mListener != null) {viewHolder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {mListener.onItemClick(pos, data);}});}}@Overridepublic void onAttachedToRecyclerView(RecyclerView recyclerView) {super.onAttachedToRecyclerView(recyclerView);RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();if(manager instanceof GridLayoutManager) {final GridLayoutManager gridManager = ((GridLayoutManager) manager);gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {@Overridepublic int getSpanSize(int position) {return getItemViewType(position) == TYPE_HEADER? gridManager.getSpanCount() : 1;}});}}@Overridepublic void onViewAttachedToWindow(RecyclerView.ViewHolder holder) {super.onViewAttachedToWindow(holder);ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();if(lp != null&& lp instanceof StaggeredGridLayoutManager.LayoutParams) {StaggeredGridLayoutManager.LayoutParams p = (StaggeredGridLayoutManager.LayoutParams) lp;p.setFullSpan(holder.getLayoutPosition() == 0);}}public int getRealPosition(RecyclerView.ViewHolder holder) {int position = holder.getLayoutPosition();return mHeaderView == null ? position : position - 1;}@Overridepublic int getItemCount() {return mHeaderView == null ? mDatas.size() : mDatas.size() + 1;}public abstract RecyclerView.ViewHolder onCreate(ViewGroup parent, final int viewType);public abstract void onBind(RecyclerView.ViewHolder viewHolder, int RealPosition, T data);public class Holder extends RecyclerView.ViewHolder {public Holder(View itemView) {super(itemView);}}public interface OnItemClickListener<T> {void onItemClick(int position, T data);}
}

我们将BaseRecyclerAdapter抽象起来,并且提供两个抽象方法onCreateonBind用来创建holder和绑定数据,而对于header做的一系列工作,我们都放到了BaseRecyclerAdapter中,而继承BaseRecyclerAdapter后,我们仅仅关心我们的holder怎么创建和数据怎么绑定就ok。例如下面代码:

/*** Created by qibin on 2015/11/7.*/
public class MyAdapter extends BaseRecyclerAdapter<String> {@Overridepublic RecyclerView.ViewHolder onCreate(ViewGroup parent, int viewType) {View layout = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);return new MyHolder(layout);}@Overridepublic void onBind(RecyclerView.ViewHolder viewHolder, int RealPosition, String data) {if(viewHolder instanceof MyHolder) {((MyHolder) viewHolder).text.setText(data);}}class MyHolder extends BaseRecyclerAdapter.Holder {TextView text;public MyHolder(View itemView) {super(itemView);text = (TextView) itemView.findViewById(R.id.text);}}
}

这样我们再用起来就简单多了,对于这样的封装,我们还算满意,再做完添加header后,相信大家对于footer也有想法了,有想法就实现它吧,扩展一下BaseRecyclerAdapter就ok啦。

好了,这篇博客就到这里吧,最后是本文代码的下载。

代码下载,戳这里


http://chatgpt.dhexx.cn/article/AuiYnYZB.shtml

相关文章

Android-第七节RecyclerView详解

目录 一、RecyclerView概述二、RecyclerView使用步骤三、布局管理器 一、RecyclerView概述 RecyclerView是support-v7包中的新组件&#xff0c;是一个强大的滑动组件&#xff0c;与经典的ListView相比&#xff0c;同样拥有item回收复用的功能&#xff0c;这一点从它的名字Recyc…

RecyclerView不显示问题

当我们使用RecyclerView控件的时候可能遇到不显示问题&#xff0c;一般分为以下情况&#xff0c; 一、RecyclerView和ScrollView嵌套使用出现RecyclerView不显示的问题&#xff0c; 首先要确保你从服务端拿到的数据不是空的&#xff0c; 1.第一种解决方式&#xff1a; 只需…

RecycleView

一. 在build.gradle中添加依赖 implementation androidx.recyclerview:recyclerview:1.1.0二. activity_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"…

Android学习之RecyclerView

RecyclerView是android-support-v7-21版本中新增的一个Widget,官方介绍RecyclerView 是 ListView 的升级版本,更加先进和灵活。 开发环境 - IDE: ADT - SDK: Android L 首先在Android SDK Manager上下载Android Support Repository, 它会在sdk/extras/android目录下生成m2…

Android——RecyclerView入门学习之RecyclerView.Adapter

学习资料&#xff1a; 鸿洋大神为RecyclerView打造通用Adapter让RecyclerView更加好用鸿洋大神Android优雅的为RecyclerView添加HeaderView和FooterView 之前使用RecyclerView.Adapter&#xff0c;基本就类似套用公式&#xff0c;死步骤&#xff0c;对Adapter感到既熟悉又陌生…

Android——RecyclerView的使用

RecyclerView 参考&#xff1a;《第一行代码》第二版——郭霖 可以用RecyclerView来显示多行列表。 recycle 循环回收再利用。 通过一个水果列表来学习RecyclerView 准备工作&#xff0c;在app/build.gradle文件中导入依赖 dependencies {implementation androidx.appcompat:a…

RecyclerView(二)—— RecyclerView的使用

RecyclerView ListView由于强大的功能&#xff0c;在过去的Android开发当中可以说是贡献卓越&#xff0c;直到今天仍然还有不计其数的程序在使用ListView。不过ListView并不是完美无缺的&#xff0c;比如如果不使用一些技巧来提升它的运行效率&#xff0c;那么ListView的性能就…

Android RecyclerView

个人回顾&#xff1a; 1.RecyclerView用来代替ListView和GridView 2.RecyclerView可以实现瀑布流布局 3.RecyclerView最出色的是它的缓存机制&#xff08;四级缓存&#xff09;&#xff0c;ListView只有2级缓存 1.RecyclerView RecyclerView是Android 5.0推出的&#xff0c;是…

Android RecyclerView原理

文章目录 从 ListView 到 RecyclerViewRecyclerView 运行机制ListView 缓存机制RecyclerView 缓存机制RecyclerView 核心机制总结 从 ListView 到 RecyclerView RecyclerView 是在 Google I/O 在2014年时推出的控件&#xff0c;在 RecyclerView 还未出现前&#xff0c;列表都是…

AndroidStudio中RecyclerView用法

去年写了几篇关于Android的基础知识点&#xff0c;没想到收到了很多朋友的好评&#xff0c;不过后来太忙了没有继续更新&#xff0c;真的蛮遗憾的。最近又要用到Android啦&#xff0c;超级无敌巨重要的RecyclerView怎么能不讲一讲呢&#xff01;&#xff01; 1.什么是Recycler…

为RecyclerView打造通用Adapter 让RecyclerView更加好用

转载请标明出处&#xff1a; http://blog.csdn.net/lmj623565791/article/details/47251585&#xff1b; 本文出自:【张鸿洋的博客】 一、概述 记得好久以前针对ListView类控件写过一篇打造万能的ListView GridView 适配器&#xff0c;如今RecyclerView异军突起&#xff0c;…

RecyclerView的基本用法

RecyclerView 是一个增强版的ListView&#xff0c;不仅可以实现和ListView同样的效果&#xff0c;还优化了ListView中存在的各种不足之处 ResyslerView 能够实现横向滚动&#xff0c;这是ListView所不能实现的 目前官方更加推荐使用RecyclerView. 1.实现垂直方向的滚动 在 …

RecyclerView安卓androidx.widget.RecyclerView

导入RecyclerView implementation com.android.support:recyclerview-v7:28.0.0或者把常用的组件都导入了&#xff0c;包括了RecyclerView implementation com.android.support:design:28.0.01.RecyclerView介绍 RecyclerView它可以说是一个增强版的ListView。ListView由于其…

RecyclerView的基本使用

RecyclerView是Android一个更强大的控件,其不仅可以实现和ListView同样的效果,还有优化了ListView中的各种不足。其可以实现数据纵向滚动,也可以实现横向滚动(ListView做不到横向滚动)。接下来讲解RecyclerView的用法。 RecyclerView 基本用法 因为RecyclerView属于新增的控件,…

RecyclerView 的使用(androidx)

文章目录 一、导包二、基本使用三、设置分割线四、自定义点击事件五、实现 GridView六、实现瀑布流七、更多效果八、RecyclerView 常见问题九、RecyclerView 和 ScrollView 嵌套的问题 一、导包 implementation androidx.recyclerview:recyclerview:1.1.0二、基本使用 2.1、首…

RecyclerView简单使用(非常详细)

星期六&#xff0c;又是撸代码的一天 作为一个初级都算不上的小白&#xff0c;一步一个脚印的学吧&#xff0c;学一个记一个 今天记录的是RecyclerView RecyclerView 简述创建布局添加 RecyclerView和每个list布局&#xff08;偏新手向&#xff0c;选择性跳过&#xff09; 创建…

androidx.recyclerview:recyclerview的使用

添加扩展 或手动修改app/build.gradle&#xff1a; 在dependencies里添加 implementation androidx.recyclerview:recyclerview:1.0.0新建布局layout item布局fruit_item.xml&#xff1a; <?xml version"1.0" encoding"utf-8"?> <LinearLayo…

Android学习之RecyclerView的使用

RecyclerView是Android 5.0推出的&#xff0c;是support-v7包中的新组件,它被用来代替ListView和GridView&#xff0c;并且能够实现瀑布流的布局&#xff0c;更加高级并且更加灵活&#xff0c;提供更为高效的回收复用机制&#xff0c;同时实现管理与视图的解耦合。 官方文档解释…

RecyclerView剖析

####简介   本文将从RecyclerView实现原理并结合源码详细分析这个强大的控件。阅读本文要求&#xff1a;1、熟悉android控件绘制&#xff0c;2、了解动画&#xff0c;3、了解Scroller.本文所示源码版本是23.2.0。 ####基本使用   RecyclerView的基本使用并不复杂&#xff…

RecyclerView的使用(一)

目录 1.RecyclerView概述 2.RecyclerView的简单使用 3.改变布局管理器&#xff0c;RecyclerView的变化 1.RecyclerView概述 在谷歌Android官网&#xff0c;给RecyclerView的描述是: 那RecyclerView凭什么要比ListView要更高级&#xff1f;更灵活&#xff1f; 答案是&#x…