View 体系
View 体系 · androidx.core
RecyclerView:让列表更新可证明Core 1.20.0-alpha01
用 ListAdapter 与 DiffUtil 区分条目身份和内容变化,管理 LayoutManager 与状态。
最后更新 2026-08-01
复制即用:最小列表
object UserDiff : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(old: User, new: User) = old.id == new.id
override fun areContentsTheSame(old: User, new: User) = old == new
}
class UserAdapter : ListAdapter<User, UserViewHolder>(UserDiff) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder =
UserViewHolder.create(parent)
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(getItem(position))
}
}
// 使用
val adapter = UserAdapter()
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = adapter
adapter.submitList(newUsers) // 提交新列表,异步 diffDiffUtil:身份 vs 内容
object UserDiff : DiffUtil.ItemCallback<User>() {
// 同一业务实体吗?(id 相同 = 同一个人)
override fun areItemsTheSame(old: User, new: User) = old.id == new.id
// 显示内容变了吗?(数据类 == 全字段比较)
override fun areContentsTheSame(old: User, new: User) = old == new
}关键:
areItemsTheSame= 身份(决定哪些条目”没动”)。areContentsTheSame= 内容(决定”动了”的条目是否重绘)。- 两者都写成引用比较(
old === new)会让原地修改的列表无法正确刷新。
ListAdapter 与 AsyncListDiffer
// ListAdapter:Adapter + AsyncListDiffer 封装
class UserAdapter : ListAdapter<User, UserViewHolder>(UserDiff)
// AsyncListDiffer:底层机制(自定义 Adapter 时用)
class UserAdapter : RecyclerView.Adapter<UserViewHolder>() {
private val differ = AsyncListDiffer(this, UserDiff)
override fun getItemCount() = differ.currentList.size
fun submitList(list: List<User>) = differ.submitList(list)
}规则:每次提交新的不可变列表;不要修改已提交的 list 或影响 UI 的对象后,再提交同一个实例。
LayoutManager:排列方式
// 线性
recyclerView.layoutManager = LinearLayoutManager(context)
// 网格
recyclerView.layoutManager = GridLayoutManager(context, 2)
// 瀑布流
recyclerView.layoutManager = StaggeredGridLayoutManager(2, VERTICAL)| LayoutManager | 场景 |
|---|---|
LinearLayoutManager | 单列列表 |
GridLayoutManager | 规则网格 |
StaggeredGridLayoutManager | 瀑布流(高度不一) |
三类状态要分开保存
| 状态 | 存放 |
|---|---|
| 业务数据 | ViewModel / Repository |
| 滚动位置 | layoutManager.onSaveInstanceState() |
| 选择状态 | 单独保存(不塞 ViewHolder) |
ViewHolder 是复用的:滚动时反复绑定不同条目,不能在其中保存”选中/滚动”这类持久状态。
常见陷阱
- Diff 写引用比较:原地修改列表刷新不了。
- 复用已提交的 list:改原 list 再 submit 同实例,diff 失效。
- 忘 LayoutManager:不设置列表空白/崩溃。
- ViewHolder 存状态:滚动后状态串到别的条目。
- 大数据用全量 notifyDataSetChanged:应 submitList 走 diff。
复制即用
class UserViewHolder(
private val binding: ItemUserBinding,
) : RecyclerView.ViewHolder(binding.root) {
fun bind(user: User, onClick: (User) -> Unit) {
binding.name.text = user.name
binding.root.setOnClickListener { onClick(user) }
}
companion object {
fun create(parent: ViewGroup): UserViewHolder {
val binding = ItemUserBinding.inflate(
LayoutInflater.from(parent.context), parent, false
)
return UserViewHolder(binding)
}
}
}
// Fragment 中使用
class UserListFragment : Fragment() {
private val adapter = UserAdapter()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = adapter
viewModel.users.observe(viewLifecycleOwner) { users ->
adapter.submitList(users)
}
}
}要点
areItemsTheSame管身份、areContentsTheSame管内容。- ListAdapter 内部 AsyncListDiffer 异步 diff;提交不可变新列表。
- LayoutManager 决定排列;滚动/选择/数据三类状态分开存。
- ViewHolder 复用,不存持久状态。