RecyclerView 部件和功能
RecyclerView
在内部处理一些任务(例如视图的滚动和回收),但本质上它是一个协调辅助类以显示集合的管理器。 RecyclerView
将任务委托给以下帮助程序类:
Adapter
– 填充项目布局(实例化布局文件的内容)并将数据绑定到在RecyclerView
中显示的视图。 适配器还会报告项点击事件。LayoutManager
– 在RecyclerView
中测量和定位项目视图,并管理视图回收的策略。ViewHolder
– 查找和存储视图引用。 视图持有者还有助于检测项视图点击。ItemDecoration
– 允许应用程序为特定视图添加特殊的绘制和布局偏移量,以便在项之间绘制分隔符、高亮显示和视觉分组边界。ItemAnimator
– 定义在项操作期间发生的动画,或者定义对适配器进行更改时发生的动画。
下图描述了 RecyclerView
、LayoutManager
和 Adapter
类之间的关系:
如图所示,LayoutManager
可以视为 Adapter
与 RecyclerView
之间的中介。 LayoutManager
代表 RecyclerView
调用 Adapter
方法。 例如,当 LayoutManager
在 RecyclerView
中为特定项位置创建新视图时,将调用 Adapter
方法。 Adapter
扩充该项的布局,并创建一个 ViewHolder
实例(未显示),以缓存对该位置的视图的引用。 当 LayoutManager
调用 Adapter
以将特定项绑定到数据集时,Adapter
会找到该项的数据,从数据集中检索它,并将其复制到关联的项视图。
在应用中使用 RecyclerView
时,需要创建以下类的派生类型:
RecyclerView.Adapter
– 为应用的数据集(特定于你的应用)提供绑定,以便将其绑定到在RecyclerView
中显示的项视图。 适配器知道如何将RecyclerView
中的每个项视图位置关联到数据源中的特定位置。 此外,适配器还处理每个项视图中内容的布局,并为每个视图创建视图持有者。 适配器还报告由项视图检测到的项点击事件。RecyclerView.ViewHolder
– 缓存项布局文件中视图的引用,以避免不必要的重复资源查找。 当用户点击视图持有者的关联项视图时,视图持有者还会安排将项点击事件转发到适配器。RecyclerView.LayoutManager
– 在RecyclerView
中定位项。 可以使用多个预定义布局管理器之一,也可以实施自己的自定义布局管理器。RecyclerView
将布局策略委托给布局管理器,因此你可以插入不同的布局管理器,而无需对应用进行重大更改。
此外,还可以选择扩展以下类,以更改应用中 RecyclerView
的外观:
RecyclerView.ItemDecoration
RecyclerView.ItemAnimator
如果不扩展 ItemDecoration
和 ItemAnimator
,RecyclerView
将使用默认实现。 本指南不介绍如何创建自定义 ItemDecoration
类和 ItemAnimator
类;有关这些类的详细信息,请参阅 RecyclerView.ItemDecoration 和 RecyclerView.ItemAnimator。
视图回收的工作原理
RecyclerView
不会为数据源中的每个项分配项视图。 相反,它只分配适合屏幕数量的项视图,并在用户滚动时重用这些项布局。 当视图首次滚动出视线时,它将经历下图所示的回收过程:
当视图不可见且不再显示时,将成为报废视图。
报废视图进入池中,成为回收视图。 此池是显示相同数据类型的视图的缓存。
显示新项时,将从回收池获取视图以供重复使用。 由于此视图必须在显示前被适配器重新绑定,因此称为脏视图。
回收脏视图:适配器查找要显示的下一项的数据,并将此数据复制到此项的视图。 从与回收视图关联的视图持有者中检索这些视图的引用。
回收的视图被添加到
RecyclerView
中即将显示在屏幕上的项列表。当用户将
RecyclerView
滚动到列表中的下一项时,回收视图会在屏幕上显示。 同时,另一个视图不可见,并按上述步骤回收。
除了项视图重用之外,RecyclerView
还使用了另一种效率优化方法:视图持有者。 视图持有者是缓存视图引用的简单类。 每次适配器填充项布局文件时,它还会创建一个相应的视图持有者。 视图持有者使用 FindViewById
来获取填充的项布局文件中视图的引用。 每次回收布局以显示新数据时,这些引用都用于将新数据加载到视图中。
布局管理器
布局管理器负责在 RecyclerView
显示中定位项;它确定呈现类型(列表或网格)、方向(项是垂直显示还是水平显示)以及项的显示顺序(正常顺序或反向顺序)。 布局管理器还负责计算 RecycleView 显示中的每个项的大小和位置。
布局管理器具有其他用途:它确定何时回收不再对用户可见的项视图的策略。 由于布局管理器知道哪些视图可见(哪些不可见),因此它处于最佳位置来决定何时可以回收视图。 为了回收视图,布局管理器通常会调用适配器,用不同的数据替换回收视图的内容,正如前面在视图回收的工作原理中所述。
可以扩展 RecyclerView.LayoutManager
来创建自己的布局管理器,也可以使用预定义的布局管理器。 RecyclerView
提供以下预定义布局管理器:
LinearLayoutManager
– 排列可以垂直滚动的列中的项,或者排列可水平滚动的行中的项。GridLayoutManager
– 在网格中显示项。StaggeredGridLayoutManager
– 在错开的网格中显示项,其中某些项具有不同的高度和宽度。
若要指定布局管理器,请实例化所选布局管理器并将其传递给 SetLayoutManager
方法。 请注意,必须指定布局管理器 – RecyclerView
在默认情况下不选择预定义的布局管理器。
有关布局管理器的详细信息,请参阅 RecyclerView.LayoutManager 类引用。
视图持有者
视图持有者是用于缓存视图引用的类。 适配器使用这些视图引用将每个视图绑定到其内容。 RecyclerView
中的每个项都有一个关联的视图持有者实例,该实例缓存该项的视图引用。 若要创建视图持有者,请使用以下步骤定义一个类来保存每个项的确切视图集:
- 子类
RecyclerView.ViewHolder
。 - 实现查找和存储视图引用的构造函数。
- 实现适配器可用于访问这些引用的属性。
基本 RecyclerView 示例中提供了 ViewHolder
实现的详细示例。
有关 RecyclerView.ViewHolder
的详细信息,请参阅 RecyclerView.ViewHolder 类引用。
适配器
RecyclerView
集成代码的大部分“繁重工作”都发生在适配器中。 RecyclerView
要求你提供派生自 RecyclerView.Adapter
的适配器来访问数据源,并使用数据源中的内容填充每个项。
由于数据源特定于应用,因此必须实现适配器功能,以了理解如何访问数据。 适配器从数据源中提取信息,并将其加载到 RecyclerView
集合中的每个项中。
下图演示了适配器如何通过视图持有者将数据源中的内容映射到 RecyclerView
中每行项内的单个视图:
适配器使用特定行项的数据加载每个 RecyclerView
行。 例如,对于行位置 P,适配器将找到数据源中 P 位置的相关数据,并将此数据复制到 RecyclerView
集合中 P 位置的行项。
例如,在上图中,适配器使用视图持有者查找位于该位置的 ImageView
和 TextView
的引用,这样用户滚动浏览集合并重复使用视图时,不必为这些视图重复调用 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 示例”中,将实现一个示例应用程序,以展示上面概述的各个部分和功能的实际代码示例。