🚀 RecyclerView 进阶优化指南

相比之前的版本,这次的改进包括:

  • 使用 DiffUtil 优化数据更新(避免 notifyDataSetChanged()
  • 使用 ViewBinding 简化 ViewHolder 代码
  • 封装 Adapter 支持 MutableList(支持增删改查)
  • 优化 onClick 事件,让 RecyclerView 更易扩展
  • 支持 Grid/瀑布流/线性布局

1️⃣ 添加 RecyclerView 依赖

确保你的 build.gradle 添加了:

1
2
3
4
dependencies {
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.viewbinding:viewbinding:7.3.1' // 使用 ViewBinding
}

2️⃣ 在 activity_main.xml 中添加 RecyclerView

1
2
3
4
5
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"/>

3️⃣ 创建数据模型

1
2
3
4
5
6
data class Diary(
val id: Int, // 方便更新和删除
val title: String,
val content: String,
val date: String
)

4️⃣ 使用 DiffUtil 优化 Adapter

🚀 DiffUtil 可以高效更新 RecyclerView,避免全量刷新,提高性能。

创建 DiaryDiffCallback

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class DiaryDiffCallback(
private val oldList: List<Diary>,
private val newList: List<Diary>
) : DiffUtil.Callback() {

override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size

// 判断是否是同一项(通常比较 ID)
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition].id == newList[newItemPosition].id
}

// 判断内容是否相同(避免不必要的刷新)
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
}

5️⃣ 优化 Adapter(使用 ViewBinding

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
class DiaryAdapter(
private var diaryList: MutableList<Diary>, // 让数据可变
private val onItemClick: (Diary) -> Unit
) : RecyclerView.Adapter<DiaryAdapter.DiaryViewHolder>() {

// 使用 ViewBinding 简化 ViewHolder
class DiaryViewHolder(val binding: ItemDiaryBinding) : RecyclerView.ViewHolder(binding.root)

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DiaryViewHolder {
val binding = ItemDiaryBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return DiaryViewHolder(binding)
}

override fun onBindViewHolder(holder: DiaryViewHolder, position: Int) {
val diary = diaryList[position]
with(holder.binding) {
tvTitle.text = diary.title
tvContent.text = diary.content
tvDate.text = diary.date
root.setOnClickListener { onItemClick(diary) } // 处理点击事件
}
}

override fun getItemCount(): Int = diaryList.size

// 🚀 使用 DiffUtil 刷新数据,避免全量刷新
fun updateData(newList: List<Diary>) {
val diffResult = DiffUtil.calculateDiff(DiaryDiffCallback(diaryList, newList))
diaryList.clear()
diaryList.addAll(newList)
diffResult.dispatchUpdatesTo(this)
}

// 增删改查方法
fun addDiary(diary: Diary) {
diaryList.add(0, diary) // 插入到第一个位置
notifyItemInserted(0)
}

fun removeDiary(position: Int) {
if (position in diaryList.indices) {
diaryList.removeAt(position)
notifyItemRemoved(position)
}
}
}

6️⃣ 创建 item_diary.xml

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
<?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"
android:padding="10dp"
android:background="@android:color/white">

<TextView
android:id="@+id/tvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textColor="@android:color/black"
android:textStyle="bold"/>

<TextView
android:id="@+id/tvContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textColor="@android:color/darker_gray"
android:maxLines="2"
android:ellipsize="end"/>

<TextView
android:id="@+id/tvDate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@android:color/holo_blue_dark"/>
</LinearLayout>

7️⃣ 在 MainActivity 绑定 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
class MainActivity : AppCompatActivity() {

private lateinit var recyclerView: RecyclerView
private lateinit var diaryAdapter: DiaryAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)

// 初始数据
val diaryList = mutableListOf(
Diary(1, "今日心情", "今天天气很好,心情愉快!", "2025-03-17"),
Diary(2, "学习 Kotlin", "RecyclerView 真的很好用!", "2025-03-16")
)

// 绑定 Adapter
diaryAdapter = DiaryAdapter(diaryList) { diary ->
Toast.makeText(this, "点击了: ${diary.title}", Toast.LENGTH_SHORT).show()
}
recyclerView.adapter = diaryAdapter

// 模拟 3 秒后刷新数据
Handler(Looper.getMainLooper()).postDelayed({
val newData = diaryList + Diary(3, "记账", "今天吃了火锅,花了 120 元", "2025-03-15")
diaryAdapter.updateData(newData)
}, 3000)
}
}

8️⃣ 可选优化

(1)网格布局

1
recyclerView.layoutManager = GridLayoutManager(this, 2) // 2 列

(2)瀑布流布局

1
recyclerView.layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)

🎯 总结

改进点 说明
使用 DiffUtil 优化数据更新,避免 notifyDataSetChanged()
使用 ViewBinding 减少 findViewById 代码,提高可读性
封装增删改查方法 addDiary()removeDiary()
支持 MutableList 让数据可变,更方便操作
支持 Grid/瀑布流布局 适配不同 UI 需求

🚀 这样写的 RecyclerView 更高效、更易扩展!你可以直接用在 Kotlin 日记本 App 里。 🎉