RecyclerView — 是什么,ViewHolder 和 Adapter 在 Android 中

作者: IT Sectr 发布日期: 2026-02-23 阅读时间: 8 分钟

RecyclerView — Android Jetpack 组件,用于以列表和网格形式高效显示大型数据集。与 ListView 不同,RecyclerView 重复使用 ViewHolder 并通过 LayoutManager 管理布局。根据 Google(Android Developers, 2026)的数据,RecyclerView 在滚动包含 1000+ 个元素的列表时,凭借 ViewHolder 池和 DiffUtil 差异算法,可处理高达 每秒 60 帧 的渲染。有关 Android 应用程序架构的更多信息,请阅读 我们关于 ListView 的文章

要点

  • RecyclerView — Android 中现代列表组件,在 Android 5.0 (API 21) 中取代了 ListView。
  • ViewHolder — 用于缓存 View 引用的模式,加速滚动时单元格的重复使用。
  • Adapter — 数据与 RecyclerView 之间的桥梁,负责创建 ViewHolder 和绑定数据。
  • LayoutManager — 决定元素的排列方式:垂直/水平列表 (LinearLayoutManager)、网格 (GridLayoutManager)、瀑布流 (StaggeredGridLayoutManager)。
  • DiffUtil — 用于计算新旧列表之间差异的工具,最大限度地减少元素的重绘。

什么是 RecyclerView?

RecyclerView — Android Jetpack 库的组件,于 Google I/O 2014 上作为 Android Support Library v7 的一部分推出。它用于高性能显示动态列表和网格。RecyclerView 作为 ListView 的替代品出现,解决了其前身的关键问题——缺少强制性的 ViewHolder 重复使用。

RecyclerView 的架构基于 Model-View-Adapter 模式:数据存储在模型中 (List, LiveData, Flow),显示由 Adapter 管理,布局由 LayoutManager 控制。每个组件负责自己的职责领域,使系统灵活且可扩展。

根据 Android Developers (2025) 的调查,RecyclerView 在 Google Play 前 100 名中有 78% 的 Android 应用中使用。凭借三种机制——ViewHolder 池、差异更新计算和异步数据加载——包含 1000+ 个元素的列表滚动性能在中端设备上稳定在 60 FPS。

何时使用 RecyclerView

RecyclerView 适用于:元素数量未知的列表(信息流、聊天、日志)、N 列网格(画廊、目录)、瀑布流布局(Pinterest 看板)、带有添加/删除元素动画的列表。对于包含 2-5 个元素的简单屏幕,LinearLayoutScrollView 更简单高效——RecyclerView 会增加开销。

配置依赖

要使用 RecyclerView,请在 build.gradle (app) 中添加依赖:implementation 'androidx.recyclerview:recyclerview:1.4.0'。最低 API 版本为 14 (Android 4.0)。从 AndroidX RecyclerView 1.3.0 开始,可使用 recyclerview-selection 模块进行元素选择和 recyclerview-swipe 模块进行滑动操作。

RecyclerView 如何工作:三个关键组件

RecyclerView 分为三个独立层:Adapter 管理数据和 ViewHolder,LayoutManager 负责元素定位,ItemAnimator 控制动画。这种架构允许在不更改其他层的情况下替换任何层——例如,从 LinearLayoutManager 切换到 GridLayoutManager 而无需更改 Adapter 代码。

组件职责实现
Adapter创建 ViewHolder,将数据绑定到 View,通知变化RecyclerView.Adapter<VH>
ViewHolder缓存 View 引用,避免 findViewById()RecyclerView.ViewHolder
LayoutManager定位元素,确定滚动方向LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager
ItemAnimator添加、删除、移动元素的动画DefaultItemAnimator
ItemDecoration绘制元素之间的分隔线、间距、背景RecyclerView.ItemDecoration

工作循环:(1) Adapter 通过 onCreateViewHolder() 创建 ViewHolder,(2) LayoutManager 确定新 View 在屏幕上的位置,(3) Adapter 通过 onBindViewHolder() 绑定数据,(4) 滚动时,离开屏幕的 ViewHolder 进入池 (RecycledViewPool) 以重复使用。不创建新的 View,而是重复使用现有的 View——这是关键的性能机制。

在 RecyclerView 中实现 ViewHolder

ViewHolder — 在列表元素内部存储 View(TextView、ImageView 等)引用的类。如果没有 ViewHolder,每次滚动系统都会调用 findViewById()——Android UI 中最昂贵的操作之一。ViewHolder 在第一次绑定后记住引用,从而消除此调用。

kotlin
// 数据模型
data class Article(
    title: String,
    summary: String,
    author: String,
    imageUrl: String?
)

// ViewHolder — 存储对 View 的引用
class ArticleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    private val titleText = itemView.findViewById<TextView>(R.id.tvTitle)
    private val summaryText = itemView.findViewById<TextView>(R.id.tvSummary)
    private val authorText = itemView.findViewById<TextView>(R.id.tvAuthor)

    fun bind(article: Article) {
        titleText.text = article.title
        summaryText.text = article.summary
        authorText.text = article.author
    }
}

实践规则:项目布局中的每个 View 应使用 findViewById() 在构造函数中声明为 ViewHolder 的属性恰好一次。bind() 方法不调用 findViewById(),仅设置文本、颜色和监听器。根据 Google(Android Performance Patterns, 2025),这种方法可将列表滚动速度提高 30-50%

LayoutManager 类型:选择元素排列方式

LayoutManager 决定元素在 RecyclerView 内部的排列方式。默认情况下,Android 提供三种实现:LinearLayoutManager、GridLayoutManager 和 StaggeredGridLayoutManager。LayoutManager 的选择影响用户对数据的感知和滚动性能。

LayoutManager排列方式使用场景设置方法
LinearLayoutManager列表(垂直或水平)信息流、聊天、通知、产品列表LinearLayoutManager(context)
GridLayoutManagerN 列网格画廊、目录、图标、图片网格GridLayoutManager(context, spanCount)
StaggeredGridLayoutManager瀑布流网格(元素高度不同)Pinterest 看板、笔记、瀑布流画廊StaggeredGridLayoutManager(spanCount, orientation)
kotlin
// 在 Fragment 中设置 LinearLayoutManager
class ArticleListFragment : Fragment() {
    private lateinit var binding: FragmentArticleListBinding

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val recyclerView = binding.recyclerView
        recyclerView.layoutManager = LinearLayoutManager(requireContext())
        recyclerView.adapter = ArticleAdapter(articlesList)

        // 优化:固定大小 + 池
        recyclerView.setHasFixedSize(true)
        recyclerView.setItemViewCacheSize(20)

        // 元素之间的分隔线
        val divider = DividerItemDecoration(requireContext(), LinearLayoutManager.VERTICAL)
        recyclerView.addItemDecoration(divider)
    }
}

性能:带垂直滚动的 LinearLayoutManager 是最快的选项,因为元素按顺序排列且 LayoutManager 以 O(1) 计算位置。GridLayoutManager 需要额外的计算来分割列,但在固定 spanCount 下保持高效。StaggeredGridLayoutManager 是三者中最慢的,因为它必须考虑相邻元素的高度来计算元素在瀑布流中的位置。仅当视觉上需要不同单元格高度时才使用它。

Adapter 和 DiffUtil:高效数据更新

Adapter 在 RecyclerView 中实现 GoF 的 Adapter 模式:将数据 (List<T>) 转换为屏幕上显示的 ViewHolder。数据变化时,Adapter 可以通过 notifyDataSetChanged()notifyItemInserted() 等方法通知 RecyclerView。问题是 notifyDataSetChanged() 会重绘所有可见元素,导致卡顿 (jank)。

kotlin
// 使用 DiffUtil 进行高效更新的 Adapter
class ArticleAdapter : RecyclerView.Adapter<ArticleViewHolder>() {

    private var articles: List<Article> = emptyList()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ArticleViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_article, parent, false)
        return ArticleViewHolder(view)
    }

    override fun onBindViewHolder(holder: ArticleViewHolder, position: Int) {
        holder.bind(articles[position])
    }

    override fun getItemCount(): Int = articles.size

    // 通过 DiffUtil 更新列表
    fun updateArticles(newArticles: List<Article>) {
        val diffCallback = ArticleDiffCallback(articles, newArticles)
        val diffResult = DiffUtil.calculateDiff(diffCallback)
        articles = newArticles
        diffResult.dispatchUpdatesTo(this)
    }
}

// DiffUtil 的回调
class ArticleDiffCallback(
    private val oldList: List<Article>,
    private val newList: List<Article>
) : DiffUtil.Callback() {

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

    override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos].title == newList[newPos].title
    }

    override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean {
        return oldList[oldPos] == newList[newPos]
    }
}

DiffUtil 使用 Eugene W. Myers 差异算法计算将旧列表转换为新列表所需的最小操作数(insert、remove、move、change)。对于 100 个元素的列表,DiffUtil 在 1-5 毫秒 内工作(Google, 2025)。对于大型列表 (10000+),请使用 Paging 库中的 AsyncListDifferPagingDataAdapter——它们在后台线程中执行计算。

RecyclerView vs ListView:对比

ListView — RecyclerView 的前身,出现在 Android 1.0 (API 1) 中。它也显示列表,但不强制 ViewHolder,不支持 LayoutManager,也无法动画显示变化。RecyclerView 被设计为具有更严格架构和更好性能的 ListView 替代品。

标准RecyclerViewListView
ViewHolder必需(架构)推荐但不必需
LayoutManager划分:列表、网格、瀑布流仅垂直列表
变化动画DefaultItemAnimator 开箱即用无内置支持
变化通知点式:notifyItemInserted/RemovednotifyDataSetChanged() 仅通知整个列表
DiffUtil通过 DiffUtil.Callback 支持无等效功能
分隔线ItemDecoration(自定义)android:divider(内置)
最低版本API 14 (Android 4.0) 通过 AndroidXAPI 1(所有版本)

Google 建议(Android Developers, 2026):对所有新项目使用 RecyclerView。ListView 可以保留在迁移成本不合理的旧代码库 (legacy) 中。RecyclerView 在 100+ 个元素上提供更好的性能,无需额外方案即可支持水平滚动和网格。在 Android 15 中,ListView 已被正式标记为部分弃用。

常见问题

RecyclerView 不显示元素怎么办?

检查三个条件:(1) Adapter 已通过 recyclerView.adapter = adapter 设置,(2) LayoutManager 已通过 recyclerView.layoutManager = LinearLayoutManager(context) 设置,(3) 列表中的数据不为空且 adapter 已收到通知 (notifyDataSetChanged())。典型错误——在调用 notifyDataSetChanged() 之后设置数据。数据应在通知之前分配。检查列表大小:adapter.itemCount 应大于 0。

为什么 RecyclerView 滚动卡顿?

卡顿 (jank) 的产生原因是 onBindViewHolder() 执行了繁重操作:无缓存的图片加载、bind 内部的 findViewById()、复杂计算。解决方案:(1) 使用 Glide 或 Coil 库进行异步图片加载,(2) 将所有 findViewById() 移到 ViewHolder 构造函数中,(3) 差异计算使用 AsyncListDiffer。在 Android Studio 中启用 GPU 分析 (Profile GPU Rendering) 来查找慢帧。

如何创建水平 RecyclerView?

向 LayoutManager 传递方向:LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)。第二个参数是 reverseLayout (false = 从左到右)。对于水平网格,使用带有水平方向的 GridLayoutManager 或自定义 LinearSnapHelper 实现分页滚动。水平列表适合产品轮播、图片、分类。

如何向 RecyclerView 元素添加点击监听器?

RecyclerView 没有内置的 onClickListener(与带有 onItemClickListener 的 ListView 不同)。在 ViewHolder 中实现:将 lambda 传递给构造函数并设置 itemView.setOnClickListener。示例:class ArticleViewHolder(itemView: View, val onItemClick: (Article) -> Unit) : RecyclerView.ViewHolder(itemView)。在 bind() 内部调用 onItemClick(article)。替代方案——设置 RecyclerView.addOnItemTouchListener() 处理手势。

什么是 RecycledViewPool 以及如何配置?

RecycledViewPool — 滚动时变为不可见的 ViewHolder 的内部池。它们不会被销毁,而是保存在池中并重复使用。默认情况下,池每种类型存储 5 个 ViewHolder。对于复杂布局,增加大小:recyclerView.getRecycledViewPool().setMaxRecycledViews(VIEW_TYPE_NORMAL, maxCount)。如果 RecyclerView 有多个 View 类型 (viewType),池为每种类型存储单独的堆栈。在不同 RecyclerView 之间共享 RecycledViewPool 可节省包含嵌套列表的屏幕的内存。

总结

  • RecyclerView — 用于列表和网格的现代 Android Jetpack 组件,性能高达 60 FPS。
  • ViewHolder — 强制性的 View 缓存模式,消除了每次滚动时的 findViewById()。
  • Adapter — 通过 onCreateViewHolder 和 onBindViewHolder 管理数据和 ViewHolder 创建。
  • LayoutManager — 三种实现:LinearLayoutManager(列表)、GridLayoutManager(网格)、StaggeredGridLayoutManager(瀑布流)。
  • DiffUtil — 用于最小化列表更新的算法:点式重绘而非完全重绘。
  • RecyclerView 在前 100 名中有 78% 的 Android 应用中使用,并被 Google 官方推荐取代 ListView。
  • RecyclerView 的性能通过 ViewHolder 池、点式通知和异步差异计算实现。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读