【 Android 】RecyclerView 使用方法总结

题外话:3月初的时候RecyclerView使用方法总结开始被我公开在GitHub上,前后反反复复的添加修改将自己对RecyclerView的认识全面的写出来。网上也有很多RecyclerView的开源库,如果你嫌麻烦可以直接用那些大神写的库,但是随着Android版本的升级那些开源库如果不更新就会出现大大小小的问题,到时候会很烦。我总结的方法是最原生最Android的,从根上告诉你这个地方应该怎样去实现。

官方指南:
https://developer.android.google.cn/guide/topics/ui/layout/recyclerview.html

示例项目已上传至GitHub(如果对你有帮助请Star,谢谢):
https://github.com/cnwutianhao/RecyclerView

文章分三大部分:入门篇进阶篇拓展篇

导入必要的库:
implementation 'com.android.support:recyclerview-v7:27.1.0'

一、入门篇

  • 纵向布局
  • 横向布局
  • 网格布局

1. 纵向布局

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/vertical

示例图:


纵向布局.gif

2. 横向布局

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/horizontal

示例图:


横向布局.gif

3. 网格布局

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/grid

示例图:


网格布局.gif

总结:
纵向布局、横向布局、网格布局的item可以说相差微乎其微,关键区别在于setLayoutManager()的写法不同:

纵向布局

recyclerview.setLayoutManager(new LinearLayoutManager(this));

或

LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerview.setLayoutManager(manager);
横向布局

LinearLayoutManager manager = new LinearLayoutManager(this);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerview.setLayoutManager(manager);
网格布局

recyclerview.setLayoutManager(new GridLayoutManager(this, 横排数量));

或

GridLayoutManager manager = new GridLayoutManager(this, 横排数量);
recyclerview.setLayoutManager(manager);

二、进阶篇

  • 点击
  • 分组
  • 顶部悬浮(吸顶)
  • 拖动
  • 滑动删除
  • 下拉刷新
  • 上拉加载
  • 双向滑动
  • 居中对齐
  • 展开和收缩
  • 瀑布流
  • 时间轴
  • 添加 Footer(包含 List 样式 和 网格样式)
  • 添加 Header(包含 List 样式 和 网格样式)

1. 点击

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/click

示例图:


点击.gif

总结:
RecyclerView的点击事件有两种写法:
① 在Adapter里面直接对控件做点击事件
② 写接口,在ActivityFragment上实现接口中定义的方法

在 Adapter 里面直接对控件做点击事件

@Override
public void onBindViewHolder(@NonNull XxxViewHolder holder, int position) {
    holder.控件名.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // TODO:
        }
    });
}
在 Adapter 里写接口

public interface OnItemClickListener {
    void onItemClick(参数类型 参数名 , ...);
}

private OnItemClickListener mListener;

public XxxAdapter(Context context, OnItemClickListener listener) {
    mContext = context;
    mListener = listener;
}

@Override
public void onBindViewHolder(@NonNull XxxViewHolder holder, int position) {
    holder.控件名.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            mListener.onItemClick(content);
        }
    });
}

在 Activity 或 Fragment 上实现接口中定义的方法

public class XxxActivity extends AppCompatActivity implements XxxAdapter.OnItemClickListener

XxxAdapter adapter = new XxxAdapter(context, this);

@Override
public void onItemClick(String content) {
    // TODO:
}

2. 分组

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/group

示例图:


分组.gif

总结:
RecyclerView分组的写法有很多种,我会在分组顶部悬浮联动中给出不同的写法。

第一种分组写法也是最简单的一种,将组名和内容写在同一个布局,在onBindViewHolder里根据位置来进行组名的显示与隐藏:

if (position == 0) {
    holder.组名.setVisibility(View.VISIBLE);
} else {
    if (list.get(position).get组().equals(list.get(position - 1).get组())) {
        holder.组名.setVisibility(View.GONE);
    } else {
        holder.组名.setVisibility(View.VISIBLE);
    }
}

3. 顶部悬浮(吸顶)

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/sticky

示例图:


顶部悬浮.gif

总结:
顶部悬浮也涉及到了分组,第二种分组写法将组名和RecyclerView同级写,item里面组名和内容写法与之前的分组类似,然后在onBindViewHolder里判断是否是第一个头部,有无头部来进行判断:

组名和 RecyclerView 同级写

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rcv_sticky"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:overScrollMode="never"
        android:scrollbars="none" />

    <include layout="@layout/sticky_include_recycle_item" />

</RelativeLayout>

item 里面写组名和内容

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <include layout="@layout/sticky_include_recycle_item" />

    <TextView
        android:id="@+id/tv_team"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:text="@string/app_name" />

</LinearLayout>

判断是否是第一个头部,有无头部来进行判断

public static final int FIRST_STICKY_VIEW = 1;
public static final int HAS_STICKY_VIEW = 2;
public static final int NONE_STICKY_VIEW = 3;

if (position == 0) {
    holder.组名.setVisibility(View.VISIBLE);
    holder.组名.setText(实体类.组);
    holder.itemView.setTag(FIRST_STICKY_VIEW);
} else {
    if (!TextUtils.equals(实体类.组, mList.get(position - 1).组)) {
        holder.组名.setVisibility(View.VISIBLE);
        holder.组名.setText(实体类.组);
        holder.itemView.setTag(HAS_STICKY_VIEW);
    } else {
        holder.组名.setVisibility(View.GONE);
        holder.itemView.setTag(NONE_STICKY_VIEW);
    }
}

4. 拖动

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/drag

示例图:


拖动.gif

总结:
关键字:ItemTouchHelper

5. 滑动删除

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/swipe

示例图:


滑动删除.gif

总结:
关键字:ItemTouchHelper

6. 下拉刷新

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/refresh

示例图:


下拉刷新.gif

总结:
关键字:swipeRefreshLayout.setOnRefreshListener

7. 上拉加载

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/load

示例图:


上拉加载.gif

总结:
关键字:recyclerview.addOnScrollListener,在onScrollStateChanged里判断RecyclerView的状态是空闲时,同时是最后一个可见的item时才加载,在onScrolled里获取最后一个可见的item

8. 双向滑动

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/slide

示例图:


双向滑动.gif

总结:
AdaptergetItemViewType里判断是哪种布局,在onBindViewHolder里分别对应自己的ViewHolder

9. 居中对齐

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/snaphelper

示例图:


居中对齐.gif

总结:
关键字:SnapHelper

10. 展开和收缩

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/expandcollapse

示例图:


展开和收缩.gif

总结:
item里将主内容和副内容写出来,通关点击item副内容现实和隐藏来达到效果:

private int expandedPosition = -1;

final boolean isExpanded = position == expandedPosition;
holder.rlChild.setVisibility(isExpanded ? View.VISIBLE : View.GONE);
holder.rlParent.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if (mViewHolder != null) {
            mViewHolder.rlChild.setVisibility(View.GONE);
            notifyItemChanged(expandedPosition);
        }
        expandedPosition = isExpanded ? -1 : holder.getAdapterPosition();
        mViewHolder = isExpanded ? null : holder;
        notifyItemChanged(holder.getAdapterPosition());
    }
});

11. 瀑布流

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/waterfall

示例图:


瀑布流.gif

总结:
关键字:StaggeredGridLayoutManager,示例代码取的是干货集中营福利的接口数据。

12. 时间轴

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/timeline

示例图:


时间轴.gif

总结:
item里写两种状态的布局,一种是当前状态的,另一种是之前状态的。在onBindViewHolder里通过判断位置现实和隐藏来实现:

 private static final int TYPE_HEADER = 0;
 private static final int TYPE_NORMAL = 1;

if (getItemViewType(position) == TYPE_HEADER) {
    holder.tvHeaderLine.setVisibility(View.INVISIBLE);
    holder.tvTime.setTextColor(Color.BLACK);
    holder.tvContext.setTextColor(Color.BLACK);
    holder.tvDot.setBackgroundResource(R.drawable.timeline_dot_header);
} else if (getItemViewType(position) == TYPE_NORMAL) {
    holder.tvHeaderLine.setVisibility(View.VISIBLE);
    holder.tvTime.setTextColor(Color.GRAY);
    holder.tvContext.setTextColor(Color.GRAY);
    holder.tvDot.setBackgroundResource(R.drawable.timeline_dot_normal);
}

13. 添加 Footer(包含 List 样式 和 网格样式)

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/footer

示例图:


为 RecyclerView 添加 Footer(List 样式).gif

为 RecyclerView 添加 Footer(网格样式).gif

总结:
RecyclerView添加底部有两种形式,一种是List型,另一种是网格型。关键代码是在getItemViewType里判断位置,然后在onCreateViewHolder里面显示不同的布局。网格布局还用到了setSpanSizeLookup

13. 添加 Header(包含 List 样式 和 网格样式)

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/header

示例图:


为 RecyclerView 添加 Header(List 样式).gif

为 RecyclerView 添加 Header(网格样式).gif

总结:
与添加底部类似。

三、拓展篇

  • 联动

左右联动

示例项目:
https://github.com/cnwutianhao/RecyclerView/tree/master/app/src/main/java/com/haocent/android/recyclerview/link

示例图:


左右联动.gif

总结:
参考 GangedRecyclerview,但是原著的代码对于那些理解RecyclerView不透彻的不是很友好,都是自定义类,上手难,我对其进行了修改,去掉所有的自定义类,达到标准的书写格式,代码看上去很安卓。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,520评论 25 707
  • 不久前学习了李笑来老师关于坚持的说法,他说,如果一件事情需要坚持才能完成的话,那么这件事多半不可能成功。坚持这个概...
    萧珩阅读 531评论 0 1
  • 工作队列 在上一个教程中,我们写了一个从一个已经命好名的队列中收发消息的程序。在这个教程中,我们将创建一个工作队列...
    番薯IT阅读 944评论 0 5
  • 9月1日,是开学的日子,是要对8月进行复盘的日子,是制定目标继续前行的日子。 8月我的成长主要体现在写作和社交两个...
    华思语阅读 428评论 4 15
  • 小区里有一个仰卧起坐的健身器材,然然从来都把它当作小型滑梯。今天然然刚走过去,有一个小男孩儿就飞奔过来,坐在健身器...
    右手戒指阅读 126评论 0 1