一. 前言

1. 使用方式

① build.gradle(Module:app)中添加依赖。

1
implementation 'androidx.recyclerview:recyclerview:1.0.0'

2. 布局文件中搜索RecyclerView下载

2. 介绍

RecyclerView是 Google 用来替代 ListViewGridView 的控件。默认提供了三个 LayoutManager

  • LinearLayoutManager:线性布局管理器,支持横向,纵向。
  • GridLayoutManager:网格布局管理器。
  • StaggeredGridLayoutManager:瀑布流式布局管理器。

二. RecyclerView.Adapter

RecyclerView使用的适配器需要继承RecyclerView.Adapter,RecyclerView.ViewHolder本身是一个抽象类,我们往往自己继承这个抽象类,然后绑定我们的布局控件对象。继承该类时必须传入一个itemView,表示这个item显示的View。

在 RecyclerView.Adapter中必须实现的三个方法:

1
2
3
4
5
6
7
8
//列表页需要知道有多少个item
public int getItemCount();

//创建一个ViewHolder,我们可以根据viewType的不同而创建不同的ViewHolder实现列表页各种不一样的item
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType);

//在ViewHolder中绑定数据
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder,int position);

三. 四个小例子

Github地址

1. LinearLayoutManager - 纵向

① 自定义的RecyclerView类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
public class XLRecyclerView extends RecyclerView {

private OnItemClickListener listener;

/**
* 构造方法:Java代码初始化
* @param context
*/
public XLRecyclerView(@NonNull Context context) {
super(context);

//初始化操作
init();
}

/**
* 构造方法:Xml代码初始化
* @param context
* @param attrs
*/
public XLRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);

//初始化操作
init();
}

/**
* 初始化操作
*/
private void init() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
this.setLayoutManager(linearLayoutManager);
this.setAdapter(new MyAdapter(DataUtil.loadData(getContext())));
}

/**
* 适配器
*/
class MyAdapter extends RecyclerView.Adapter{
//存储数据
private List<FriendBean> friends;

//构造方法
public MyAdapter(List<FriendBean> friends) {
this.friends = friends;
}

@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//加载布局
View inflate = LayoutInflater.from(parent.getContext()).inflate(R.layout.friend_layout, parent, false);

//返回布局
return new MyViewHolder(inflate);
}

@Override
public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
//加载数据
final FriendBean friend = friends.get(position);

//设置数据
MyViewHolder myViewHolder = (MyViewHolder) holder;
myViewHolder.icon_view.setImageResource(friend.getIcon_id());
myViewHolder.name_view.setText(friend.getName());
myViewHolder.mode_view.setText(friend.getMode());

//设置点击事件
myViewHolder.icon_view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null){
listener.onItemClick(position);
}
}
});
}

@Override
public int getItemCount() {
return friends.size();
}
}

/**
* ViewHolder
*/
static class MyViewHolder extends RecyclerView.ViewHolder{
//显示好友的头像
ImageView icon_view;
//显示好友的名称
TextView name_view;
//显示好友的动态
TextView mode_view;

/**
* 构造方法
* @param itemView
*/
public MyViewHolder(@NonNull View itemView) {
super(itemView);

//绑定控件
icon_view = itemView.findViewById(R.id.friend_icon);
name_view = itemView.findViewById(R.id.friend_name);
mode_view = itemView.findViewById(R.id.friend_mode);
}
}

/**
* 监听item被点击
*/
public interface OnItemClickListener {
void onItemClick(int position);
}

public void setOnItemClickListener(OnItemClickListener listener) {
this.listener = listener;
}
}

② item的布局文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?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="wrap_content"
android:orientation="horizontal"
android:paddingTop="10dp"
>

<LinearLayout
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:background="#009900"
>
<swu.xl.linear_ver.XLImageView
android:id="@+id/friend_icon"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_launcher_foreground"
android:scaleType="fitXY"
/>

<LinearLayout
android:layout_marginStart="20dp"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1"
>

<TextView
android:layout_marginTop="15dp"
android:id="@+id/friend_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="20sp"
android:text="姓名"
/>

<TextView
android:layout_marginTop="10dp"
android:id="@+id/friend_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="动态"
/>

</LinearLayout>
</LinearLayout>

<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="#000000"
android:layout_below="@+id/main"
/>

</RelativeLayout>

③ 模型类,以及数据加载类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class FriendBean {
//图片资源id
private int icon_id;
//好友名称
private String name;
//好友动态
private String mode;

//构造方法
public FriendBean(int icon_id, String name, String mode) {
this.icon_id = icon_id;
this.name = name;
this.mode = mode;
}

//set,get方法
public int getIcon_id() {
return icon_id;
}

public void setIcon_id(int icon_id) {
this.icon_id = icon_id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getMode() {
return mode;
}

public void setMode(String mode) {
this.mode = mode;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class DataUtil {
/**
* 加载数据
*/
public static List<FriendBean> loadData(Context context){
//图片资源数据
int[] icon_ids = new int[]{
R.drawable.friend1,
R.drawable.friend2,
R.drawable.friend3,
R.drawable.friend4,
R.drawable.friend5,
R.drawable.friend6,
R.drawable.friend7,
R.drawable.friend8,
R.drawable.friend9,
R.drawable.friend10
};

//名字数据
String[] names = context.getResources().getStringArray(R.array.name);

//动态数据
String[] modes = context.getResources().getStringArray(R.array.mode);

//初始化数据
List<FriendBean> friends = new ArrayList<>();
for (int i = 0; i < 10; i++) {
FriendBean friend = new FriendBean(icon_ids[i],names[i],modes[i]);

friends.add(friend);
}

return friends;
}
}

④ 具体使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<swu.xl.linear_ver.XLRecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

</RelativeLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

XLRecyclerView recyclerView = findViewById(R.id.recycler);
recyclerView.setOnItemClickListener(new XLRecyclerView.OnItemClickListener() {
@Override
public void onItemClick(int position) {
Toast.makeText(MainActivity.this, DataUtil.loadData(MainActivity.this).get(position).getName(), Toast.LENGTH_SHORT).show();
}
});
}
}

2. LinearLayoutManager - 横向

只需要修改一处地方:

1
2
3
4
5
6
7
8
9
/**
* 初始化操作
*/
private void init() {
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
this.setLayoutManager(linearLayoutManager);
this.setAdapter(new MyAdapter(DataUtil.loadData(getContext())));
}

3. GridLayoutManager

① 修改了item布局文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
>

<LinearLayout
android:id="@+id/main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#009900"
android:orientation="vertical"
>
<swu.xl.grid.XLImageView
android:id="@+id/friend_icon"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_launcher_foreground"
android:scaleType="fitXY"
/>

<LinearLayout
android:layout_marginStart="20dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="1"
>

<TextView
android:layout_marginTop="15dp"
android:id="@+id/friend_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="20sp"
android:text="姓名"
/>

<TextView
android:layout_marginTop="10dp"
android:id="@+id/friend_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="动态"
/>

</LinearLayout>
</LinearLayout>

</RelativeLayout>

② 更换 LayoutManager

1
2
3
4
5
6
7
8
/**
* 初始化操作
*/
private void init() {
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(),4);
this.setLayoutManager(gridLayoutManager);
this.setAdapter(new MyAdapter(DataUtil.loadData(getContext())));
}

4. StaggeredGridLayoutManager

① 修改了item布局文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="10dp"
>

<LinearLayout
android:id="@+id/main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#009900"
android:orientation="vertical"
>
<swu.xl.grid.XLImageView
android:id="@+id/friend_icon"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_launcher_foreground"
android:scaleType="fitXY"
/>

<LinearLayout
android:layout_marginStart="20dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_weight="1"
>

<TextView
android:layout_marginTop="15dp"
android:id="@+id/friend_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:textSize="20sp"
android:text="姓名"
/>

<TextView
android:layout_marginTop="10dp"
android:id="@+id/friend_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="动态"
/>

</LinearLayout>
</LinearLayout>

</RelativeLayout>

② 更换 LayoutManager

1
2
3
4
5
6
7
8
/**
* 初始化操作
*/
private void init() {
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL);
this.setLayoutManager(staggeredGridLayoutManager);
this.setAdapter(new MyAdapter(DataUtil.loadData(getContext())));
}

四. 常用的属性

1
2
3
4
5
6
7
8
9
10
//设置RecyclerView滑动到顶底部无波纹
android:overScrollMode="never"

//设置无滚动条
android:scrollbars="none"

//解决RecyclerView嵌套RecyclerView滑动到边缘冲突配置
//在最顶层RecyclerView的父布局配置两个属性:
android:focusable="true"
android:focusableInTouchMode="true"

五. 问题解决

1. 瀑布流样式和网格样式的区别

瀑布流就是设置下几行或者几列,然后设定下方向而已。网格样式时不也一样是设置下几行或几列,也一样是要再设置个方向。那么为什么瀑布流不可以直接用网格样式来实现呢?它们两者有什么区别么?

下面以两者都设置为竖直方向多列的样式来区分:

1、网格样式每一行中的所有 item 高度是一致的,不同行可以不一样,但同行的都是一样的,因此它就实现不了瀑布流的样式了;瀑布流所有的 item 高度都允许不一样,所有能实现瀑布流样式。

2、网格样式支持 item 占据多列的宽度;瀑布流支持 item 占据总列数的宽度,不支持只占据其中几列。

3、当设置为水平方向样式时,以上结论中行列对调,宽度高度对调。

2. 使用RecyclerView的优点

  • 提供 ViewHolder模式,使得开发者真正操作的是 ViewHolder,而不是像 ListView中的 GridView,需要开发者自己 setTagview.getTag

  • 同时支持列表布局和网格布局,而 ListView只能支持列表布局,网格布局需要用 GridView

  • 支持瀑布流布局。我们不在需要为实现瀑布流效果而苦恼。

  • 操作动画。在对列表进行增加、删除时的动画。并且 Adapter提供了增加删除某个 item的方法。

  • 性能与拓展性。RecyclerView听起来像是回收的view,事实上, RecyclerView本身就不关心 View相关的显示、 View显示什么内容( ViewHolder来管理), View怎么摆放( LayoutManager来管理),也不关心动画( ItemAmator来管理),甚至连分割线它都不管(由 ItemDecoration来管理) 而它关心 View的回收复用,这跟性能有关系。所以名字用 Recycle也是有道理的。这样的好处是,把各个环节工作交付给了不同的类,类似“插件化”。特别方便拓展,自定义各种各样的差异化,而从这其中解耦出来。

3. 使用RecyclerView的缺点

需要自己实现 OnItemClickListener点击事件。

参考文章

“大哥”已就位,赶紧来膜拜吧!—RecyclerView详解

RecyclerView