RecyclerView 部件和功能

RecyclerView 在内部处理一些任务(例如视图的滚动和回收),但本质上它是一个协调辅助类以显示集合的管理器。 RecyclerView 将任务委托给以下帮助程序类:

  • Adapter – 填充项目布局(实例化布局文件的内容)并将数据绑定到在 RecyclerView 中显示的视图。 适配器还会报告项点击事件。

  • LayoutManager – 在 RecyclerView 中测量和定位项目视图,并管理视图回收的策略。

  • ViewHolder – 查找和存储视图引用。 视图持有者还有助于检测项视图点击。

  • ItemDecoration – 允许应用程序为特定视图添加特殊的绘制和布局偏移量,以便在项之间绘制分隔符、高亮显示和视觉分组边界。

  • ItemAnimator – 定义在项操作期间发生的动画,或者定义对适配器进行更改时发生的动画。

下图描述了 RecyclerViewLayoutManagerAdapter 类之间的关系:

Diagram of RecyclerView containing LayoutManager, using Adapter to access Data Set

如图所示,LayoutManager 可以视为 AdapterRecyclerView 之间的中介。 LayoutManager 代表 RecyclerView 调用 Adapter 方法。 例如,当 LayoutManagerRecyclerView 中为特定项位置创建新视图时,将调用 Adapter 方法。 Adapter 扩充该项的布局,并创建一个 ViewHolder 实例(未显示),以缓存对该位置的视图的引用。 当 LayoutManager 调用 Adapter 以将特定项绑定到数据集时,Adapter 会找到该项的数据,从数据集中检索它,并将其复制到关联的项视图。

在应用中使用 RecyclerView 时,需要创建以下类的派生类型:

  • RecyclerView.Adapter – 为应用的数据集(特定于你的应用)提供绑定,以便将其绑定到在 RecyclerView 中显示的项视图。 适配器知道如何将 RecyclerView 中的每个项视图位置关联到数据源中的特定位置。 此外,适配器还处理每个项视图中内容的布局,并为每个视图创建视图持有者。 适配器还报告由项视图检测到的项点击事件。

  • RecyclerView.ViewHolder – 缓存项布局文件中视图的引用,以避免不必要的重复资源查找。 当用户点击视图持有者的关联项视图时,视图持有者还会安排将项点击事件转发到适配器。

  • RecyclerView.LayoutManager – 在 RecyclerView 中定位项。 可以使用多个预定义布局管理器之一,也可以实施自己的自定义布局管理器。 RecyclerView 将布局策略委托给布局管理器,因此你可以插入不同的布局管理器,而无需对应用进行重大更改。

此外,还可以选择扩展以下类,以更改应用中 RecyclerView 的外观:

  • RecyclerView.ItemDecoration
  • RecyclerView.ItemAnimator

如果不扩展 ItemDecorationItemAnimatorRecyclerView 将使用默认实现。 本指南不介绍如何创建自定义 ItemDecoration 类和 ItemAnimator 类;有关这些类的详细信息,请参阅 RecyclerView.ItemDecorationRecyclerView.ItemAnimator

视图回收的工作原理

RecyclerView 不会为数据源中的每个项分配项视图。 相反,它只分配适合屏幕数量的项视图,并在用户滚动时重用这些项布局。 当视图首次滚动出视线时,它将经历下图所示的回收过程:

Diagram illustrating the six steps of view recycling

  1. 当视图不可见且不再显示时,将成为报废视图

  2. 报废视图进入池中,成为回收视图。 此池是显示相同数据类型的视图的缓存。

  3. 显示新项时,将从回收池获取视图以供重复使用。 由于此视图必须在显示前被适配器重新绑定,因此称为脏视图

  4. 回收脏视图:适配器查找要显示的下一项的数据,并将此数据复制到此项的视图。 从与回收视图关联的视图持有者中检索这些视图的引用。

  5. 回收的视图被添加到 RecyclerView 中即将显示在屏幕上的项列表。

  6. 当用户将 RecyclerView 滚动到列表中的下一项时,回收视图会在屏幕上显示。 同时,另一个视图不可见,并按上述步骤回收。

除了项视图重用之外,RecyclerView 还使用了另一种效率优化方法:视图持有者。 视图持有者是缓存视图引用的简单类。 每次适配器填充项布局文件时,它还会创建一个相应的视图持有者。 视图持有者使用 FindViewById 来获取填充的项布局文件中视图的引用。 每次回收布局以显示新数据时,这些引用都用于将新数据加载到视图中。

布局管理器

布局管理器负责在 RecyclerView 显示中定位项;它确定呈现类型(列表或网格)、方向(项是垂直显示还是水平显示)以及项的显示顺序(正常顺序或反向顺序)。 布局管理器还负责计算 RecycleView 显示中的每个项的大小和位置。

布局管理器具有其他用途:它确定何时回收不再对用户可见的项视图的策略。 由于布局管理器知道哪些视图可见(哪些不可见),因此它处于最佳位置来决定何时可以回收视图。 为了回收视图,布局管理器通常会调用适配器,用不同的数据替换回收视图的内容,正如前面在视图回收的工作原理中所述。

可以扩展 RecyclerView.LayoutManager 来创建自己的布局管理器,也可以使用预定义的布局管理器。 RecyclerView 提供以下预定义布局管理器:

  • LinearLayoutManager – 排列可以垂直滚动的列中的项,或者排列可水平滚动的行中的项。

  • GridLayoutManager – 在网格中显示项。

  • StaggeredGridLayoutManager – 在错开的网格中显示项,其中某些项具有不同的高度和宽度。

若要指定布局管理器,请实例化所选布局管理器并将其传递给 SetLayoutManager 方法。 请注意,必须指定布局管理器 – RecyclerView 在默认情况下不选择预定义的布局管理器。

有关布局管理器的详细信息,请参阅 RecyclerView.LayoutManager 类引用

视图持有者

视图持有者是用于缓存视图引用的类。 适配器使用这些视图引用将每个视图绑定到其内容。 RecyclerView 中的每个项都有一个关联的视图持有者实例,该实例缓存该项的视图引用。 若要创建视图持有者,请使用以下步骤定义一个类来保存每个项的确切视图集:

  1. 子类 RecyclerView.ViewHolder
  2. 实现查找和存储视图引用的构造函数。
  3. 实现适配器可用于访问这些引用的属性。

基本 RecyclerView 示例中提供了 ViewHolder 实现的详细示例。 有关 RecyclerView.ViewHolder 的详细信息,请参阅 RecyclerView.ViewHolder 类引用

适配器

RecyclerView 集成代码的大部分“繁重工作”都发生在适配器中。 RecyclerView 要求你提供派生自 RecyclerView.Adapter 的适配器来访问数据源,并使用数据源中的内容填充每个项。 由于数据源特定于应用,因此必须实现适配器功能,以了理解如何访问数据。 适配器从数据源中提取信息,并将其加载到 RecyclerView 集合中的每个项中。

下图演示了适配器如何通过视图持有者将数据源中的内容映射到 RecyclerView 中每行项内的单个视图:

Diagram illustrating Adapter connecting Data Source to ViewHolders

适配器使用特定行项的数据加载每个 RecyclerView 行。 例如,对于行位置 P,适配器将找到数据源中 P 位置的相关数据,并将此数据复制到 RecyclerView 集合中 P 位置的行项。 例如,在上图中,适配器使用视图持有者查找位于该位置的 ImageViewTextView 的引用,这样用户滚动浏览集合并重复使用视图时,不必为这些视图重复调用 FindViewById

实现适配器时,必须重写以下 RecyclerView.Adapter 方法:

  • OnCreateViewHolder – 实例化项布局文件和视图持有者。

  • OnBindViewHolder – 将指定位置的数据加载到存储在给定视图持有者中的视图引用中。

  • ItemCount – 返回数据源中的项数。

布局管理器在定位 RecyclerView 中的项时调用这些方法。

通知 RecyclerView 数据更改

当数据源的内容发生更改时,RecyclerView 不会自动更新其显示;适配器必须在数据集中发生更改时通知 RecyclerView。 数据集可以以多种方式改变;例如,项的内容可以更改,或者数据的整体结构可能发生变化。 RecyclerView.Adapter 提供了许多可以调用的方法,以便 RecyclerView 以最有效的方式响应数据更改:

  • NotifyItemChanged – 指示位于指定位置的项已更改。

  • NotifyItemRangeChanged – 指示指定位置范围内的项已更改。

  • NotifyItemInserted – 指示新插入指定位置中的项。

  • NotifyItemRangeInserted – 指示新插入了指定位置范围内的项。

  • NotifyItemRemoved – 指示已移除指定位置中的项。

  • NotifyItemRangeRemoved – 指示移除了指定位置范围内的项。

  • NotifyDataSetChanged – 指示数据集已更改(强制进行完整更新)。

如果确切地知道数据集的更改方式,则可以调用上述适当方法以最有效的方式刷新 RecyclerView。 如果不知道数据集的更改方式,可以调用 NotifyDataSetChanged,但这样效率要低得多,因为 RecyclerView 必须刷新用户可见的所有视图。 有关这些方法的详细信息,请参阅 RecyclerView.Adapter

在下一个主题“基本 RecyclerView 示例”中,将实现一个示例应用程序,以展示上面概述的各个部分和功能的实际代码示例。