什么是 RecyclerView?想象你在翻看一本很厚的相册:
- 你的眼睛一次只能看到当前打开的这一页(屏幕可见区域)
- 翻页时,你看到新的照片,之前的照片就看不到了(视图回收)
- 你不需要一次性把所有照片都拿出来(内存优化)RecyclerView 就是这样工作的:
- 只创建屏幕可见数量的 item 视图
- 当用户滑动时,复用已有的视图,只更新显示的数据
- 这样就能节省大量内存,提高性能
2️⃣ 基础概念解析
2.1 三大核心组件
1.Adapter(适配器)
- 就像一个服务员,负责:
- 准备菜单(创建视图)
- 上菜(绑定数据到视图)
- 记录有多少道菜(数据总数)
2.ViewHolder(视图持有者)
- 像是一个托盘,用来盛放视图组件
- 避免重复 findViewById,提高性能
3.LayoutManager(布局管理器)
- 像是餐厅的桌子摆放方式:
- 直线排列(LinearLayoutManager)
- 网格排列(GridLayoutManager)
- 错落排列(StaggeredGridLayoutManager)
3️⃣ 实际代码示例(以相册为例)
3.1 首先,定义每个 item 的布局(item_photo.xml):
xml version="1.0" encoding="utf-8"?>
<
androidx.cardview.widget.CardView
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:layout_margin
=
"8dp"
>
<
LinearLayout
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:orientation
=
"vertical"
>
<
ImageView
android:id
=
"@+id/imageView"
android:layout_width
=
"match_parent"
android:layout_height
=
"200dp"
android:scaleType
=
"centerCrop"
/>
<
TextView
android:id
=
"@+id/textDescription"
android:layout_width
=
"match_parent"
android:layout_height
=
"wrap_content"
android:padding
=
"8dp"
/>
LinearLayout>
androidx.cardview.widget.CardView>
3.2 创建 ViewHolder(定义托盘):
// ViewHolder 就像托盘,用来存放 item 中的各个视图组件
class
PhotoViewHolder
extends
RecyclerView
.
ViewHolder
{
// 声明需要操作的视图组件
ImageView
imageView;
// 照片
TextView
textDescription;
// 描述文字
// 构造方法,找到所有需要的视图组件
PhotoViewHolder
(
@
NonNull
View
itemView
)
{
super
(
itemView
)
;
// 初始化视图组件,找到对应的视图
imageView = itemView.
findViewById
(
R.id.imageView
)
;
textDescription = itemView.
findViewById
(
R.id.textDescription
)
;
}
}
3.3 创建 Adapter(适配器):
class
PhotoAdapter
extends
RecyclerView
.
Adapter
<
PhotoViewHolder
>
{
// 数据源
private
List
<
PhotoItem
> photoList;
// 构造方法,传入数据
PhotoAdapter
(
List
<
PhotoItem
>
photoList
)
{
this
.photoList = photoList;
}
// 1. 创建新的 ViewHolder(准备新的托盘)
@
Override
public
PhotoViewHolder
onCreateViewHolder
(
ViewGroup
parent
,
int
viewType
)
{
// 将 XML 布局文件转换为视图对象
View
view = LayoutInflater.
from
(
parent.
getContext
()
)
.
inflate
(
R.layout.item_photo, parent, false
)
;
// 创建并返回 ViewHolder
return
new
PhotoViewHolder
(
view
)
;
}
// 2. 绑定数据到 ViewHolder(将数据放到托盘上)
@
Override
public
void
onBindViewHolder
(
PhotoViewHolder
holder
,
int
position
)
{
// 获取当前位置的数据
PhotoItem
photo = photoList.
get
(
position
)
;
// 设置图片
Glide.
with
(
holder.imageView
)
.
load
(
photo.
getImageUrl
()
)
.
into
(
holder.imageView
)
;
// 设置描述文字
holder.textDescription.
setText
(
photo.
getDescription
()
)
;
// 设置点击事件
holder.itemView.
setOnClickListener
(
v
->
{
// 处理点击事件,比如跳转到详情页
Intent
intent =
new
Intent
(
v.
getContext
()
, PhotoDetailActivity.class
)
;
intent.
putExtra
(
"photo_url"
, photo.
getImageUrl
()
)
;
v.
getContext
()
.
startActivity
(
intent
)
;
}
)
;
}
// 3. 返回数据总数(告诉系统有多少个 item)
@
Override
public
int
getItemCount
()
{
return
photoList.
size
()
;
}
}
3.4 在 Activity 中使用:
public
class
MainActivity
extends
AppCompatActivity
{
private
RecyclerView
recyclerView;
private
PhotoAdapter
adapter;
private
List
<
PhotoItem
> photoList;
@
Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R.layout.activity_main
)
;
// 1. 初始化数据
photoList =
new
ArrayList
<>
()
;
// 添加一些测试数据
photoList.
add
(
new
PhotoItem
(
"图片1"
,
"这是第一张图片"
)
)
;
photoList.
add
(
new
PhotoItem
(
"图片2"
,
"这是第二张图片"
)
)
;
// 2. 找到 RecyclerView
recyclerView =
findViewById
(
R.id.recyclerView
)
;
// 3. 创建适配器
adapter =
new
PhotoAdapter
(
photoList
)
;
// 4. 设置布局管理器(决定如何排列 item)
recyclerView.
setLayoutManager
(
new
GridLayoutManager
(
this
,
2
)
)
;
// 2列网格布局
// 5. 设置适配器
recyclerView.
setAdapter
(
adapter
)
;
}
}
4️⃣ 常用操作
4.1 添加新数据:
// 添加一条新数据 PhotoItem newPhoto = new PhotoItem("新图片", "新描述"); photoList.add(newPhoto); // 通知适配器在末尾添加了一条数据 adapter.notifyItemInserted(photoList.size() - 1);
4.2 删除数据:
// 删除指定位置的数据 photoList.remove(position); // 通知适配器数据已删除 adapter.notifyItemRemoved(position);
4.3 更新数据:
// 更新指定位置的数据 photoList.get(position).setDescription("新的描述"); // 通知适配器数据已更改 adapter.notifyItemChanged(position);
5️⃣ 布局管理器详解
5.1 线性布局(LinearLayoutManager):
// 垂直列表 recyclerView.setLayoutManager(new LinearLayoutManager(this)); // 水平列表 recyclerView.setLayoutManager( new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) );
5.2 网格布局(GridLayoutManager):
// 2列网格 recyclerView.setLayoutManager(new GridLayoutManager(this, 2)); // 3列网格 recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
5.3 瀑布流布局(StaggeredGridLayoutManager):
// 2列瀑布流
recyclerView.setLayoutManager(
new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
);
6️⃣ 优化技巧
1.设置固定大小:
// 如果列表项的大小是固定的,设置这个可以提高性能
recyclerView.
setHasFixedSize
(
true
)
;
2.添加分割线:
// 添加简单的分割线
DividerItemDecoration
divider =
new
DividerItemDecoration
(
this
, DividerItemDecoration.VERTICAL
)
;
recyclerView.
addItemDecoration
(
divider
)
;
3.设置点击事件:
// 在 ViewHolder 中设置点击事件
holder.itemView.
setOnClickListener
(
v
->
{
int
position = holder.
getAdapterPosition
()
;
PhotoItem
photo = photoList.
get
(
position
)
;
// 处理点击事件
Toast.
makeText
(
v.
getContext
()
,
"点击了: "
+ photo.
getDescription
()
, Toast.LENGTH_SHORT
)
.
show
()
;
}
)
;
7️⃣ 常见问题解答
1.为什么要使用 ViewHolder?
- 避免重复 findViewById,提高性能
- 方便管理每个 item 的视图组件
- Android 强制要求使用 ViewHolder 模式
2.如何处理点击事件?
- 可以在 ViewHolder 中设置
- 可以通过接口回调到 Activity 中处理
- 可以直接在 onBindViewHolder 中设置
3.数据更新为什么要调用 notify 方法?
- 通知 RecyclerView 数据发生了变化
- RecyclerView 才知道需要刷新界面
- 不同的 notify 方法有不同的刷新效果