RecyclerView 是 Android 提供的一个用于高效显示大量数据的视图组件。它比早期的 ListView 和 GridView 更加灵活和强大。为了更好地理解 RecyclerView,我们需要深入了解它的工作原理,包括其核心组件和主要流程。
一、核心组件
RecyclerView 的核心组件包括:
- Adapter:用于提供数据和创建
ViewHolder。 - ViewHolder:用于缓存视图的引用,避免重复查找视图。
- LayoutManager:负责布局视图和回收视图。
- Recycler:管理视图的缓存和回收。
- ItemDecoration:用于装饰
RecyclerView的子视图。 - ItemAnimator:处理视图的添加、移除和移动的动画。
二、工作流程
1. Adapter
Adapter 的主要作用是创建新的 ViewHolder 对象以及绑定数据到 ViewHolder 上。Adapter 有三个主要方法:
onCreateViewHolder(ViewGroup parent, int viewType): 创建一个新的ViewHolder。onBindViewHolder(ViewHolder holder, int position): 将数据绑定到指定位置的ViewHolder。getItemCount(): 返回数据集的总数。
2. ViewHolder
ViewHolder 是一个静态内部类,通常用于缓存视图,以避免每次都调用 findViewById。它的主要作用是提高 RecyclerView 的性能。
kotlin
复制代码
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
}
3. LayoutManager
LayoutManager 负责测量和定位 RecyclerView 的所有子视图,处理视图的回收和重用。RecyclerView 提供了三种内置的 LayoutManager:
LinearLayoutManager:以垂直或水平列表方式排列项目。GridLayoutManager:以网格方式排列项目。StaggeredGridLayoutManager:以交错网格方式排列项目。
4. Recycler
Recycler 负责管理 ViewHolder 的缓存,包括从 Scrap 缓存和 Recycler 缓存中获取 ViewHolder 以复用已有的视图。
5. ItemDecoration
ItemDecoration 用于在 RecyclerView 的子视图之间添加装饰效果,如分隔线或边距。
6. ItemAnimator
ItemAnimator 处理 RecyclerView 子视图的添加、删除和移动的动画。默认的 DefaultItemAnimator 提供了基本的动画效果。
三、工作原理
1. 初始化
当 RecyclerView 初始化时,会创建一个 Recycler 和一个 LayoutManager。LayoutManager 决定了 RecyclerView 的布局方式,如线性布局、网格布局等。
2. 布局阶段
在 RecyclerView 的布局阶段,LayoutManager 负责测量和定位子视图。LayoutManager 会从 Recycler 获取 ViewHolder,如果没有可用的 ViewHolder,则通过 Adapter 创建一个新的。
3. 数据绑定
当 ViewHolder 被创建或复用时,RecyclerView 会调用 Adapter 的 onBindViewHolder 方法,将数据绑定到 ViewHolder 上。
4. 回收机制
当视图滚动出屏幕时,RecyclerView 会将这些视图移出可视范围,并将其放入 Recycler 的缓存中,以便下次滚动回来时复用这些视图。
5. 缓存管理
Recycler 维护两种缓存:
- Scrap 缓存:保存当前屏幕上不可见但最近使用的视图。
- Recycler 缓存:保存已经从屏幕上移除的视图。
6. 动画处理
当数据发生变化时,RecyclerView 会使用 ItemAnimator 来处理视图的动画效果,如添加、删除和移动视图时的动画。
四、示例代码
下面是一个使用 RecyclerView 的简单示例:
kotlin
复制代码
class MyAdapter(private val items: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.textView.text = items[position]
}
override fun getItemCount(): Int = items.size
}
class MyActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
val items = listOf("Item 1", "Item 2", "Item 3")
adapter = MyAdapter(items)
recyclerView.adapter = adapter
}
}
RecyclerView 的缓存机制通过 Recycler 类来实现,它管理和维护 ViewHolder 的缓存以提高性能。Recycler 有两种主要的缓存:
- Scrap 缓存:存储当前屏幕上不可见但最近使用的视图。
- Recycler 缓存:存储已经从屏幕上移除的视图。
下面详细解释这两种缓存的实现原理,并提供相应的代码示例。
一、Scrap 缓存
Scrap 缓存用于存储当前屏幕上不可见但最近使用的视图。RecyclerView 在布局过程中会将这些视图放入 Scrap 缓存中,以便快速复用。
kotlin
复制代码
class RecyclerView {
private val mRecycler = Recycler()
inner class Recycler {
private val mAttachedScrap = ArrayList<ViewHolder>()
fun getScrapViewForPosition(position: Int): ViewHolder? {
for (i in mAttachedScrap.indices) {
val holder = mAttachedScrap[i]
if (holder.layoutPosition == position) {
mAttachedScrap.removeAt(i)
return holder
}
}
return null
}
fun scrapView(view: View) {
val holder = getChildViewHolder(view)
holder.setScrapContainer(this, true)
mAttachedScrap.add(holder)
}
}
fun getChildViewHolder(view: View): ViewHolder {
// 获取 view 对应的 ViewHolder
}
}
在布局过程中,如果一个视图变得不可见,RecyclerView 会将其放入 Scrap 缓存中。在下一次布局时,如果需要复用该视图,可以从 Scrap 缓存中快速获取。
二、Recycler 缓存
Recycler 缓存用于存储已经从屏幕上移除的视图。当一个视图完全从屏幕上移除时,RecyclerView 会将其放入 Recycler 缓存中。
kotlin
复制代码
class RecyclerView {
private val mRecycler = Recycler()
inner class Recycler {
private val mCachedViews = ArrayList<ViewHolder>()
fun getViewForPosition(position: Int): ViewHolder {
// 先从 Scrap 缓存中获取 ViewHolder
val scrapView = getScrapViewForPosition(position)
if (scrapView != null) {
return scrapView
}
// 再从 Recycler 缓存中获取 ViewHolder
for (i in mCachedViews.indices) {
val holder = mCachedViews[i]
if (holder.layoutPosition == position) {
mCachedViews.removeAt(i)
return holder
}
}
// 最后如果缓存中没有,则创建新的 ViewHolder
return createViewHolder()
}
fun recycleView(view: View) {
val holder = getChildViewHolder(view)
if (holder.isScrap) {
// 如果视图在 Scrap 缓存中,先移除
mAttachedScrap.remove(holder)
} else {
// 否则将视图放入 Recycler 缓存中
mCachedViews.add(holder)
}
}
private fun createViewHolder(): ViewHolder {
// 创建新的 ViewHolder
}
}
fun getChildViewHolder(view: View): ViewHolder {
// 获取 view 对应的 ViewHolder
}
}
RecyclerView缓存原理
当 RecyclerView 需要显示新的数据项时,会优先从 Scrap 缓存中获取 ViewHolder,如果没有再从 Recycler 缓存中获取,如果还没有,则创建新的 ViewHolder。使用
使用到的设计模式
1. 设计模式
1.1 ViewHolder 模式
ViewHolder 模式用于缓存视图引用,避免重复调用 findViewById,从而提高性能。这一模式在 RecyclerView 中通过 ViewHolder 类实现。
kotlin
复制代码
class MyAdapter(private val items: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.textView.text = items[position]
}
override fun getItemCount(): Int = items.size
}
1.2 Adapter 模式
Adapter 模式用于将数据源适配到视图组件。在 RecyclerView 中,通过 Adapter 类实现,负责将数据绑定到 ViewHolder。
kotlin
复制代码
class MyAdapter(private val items: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
// ViewHolder 定义略
// onCreateViewHolder、onBindViewHolder、getItemCount 方法略
}
1.3 责任链模式
RecyclerView 的布局和绘制过程通过 LayoutManager、ItemDecoration、ItemAnimator 等组件的协调配合,类似于责任链模式。每个组件在自己的职责范围内处理相应的任务。
1.4 工厂模式
RecyclerView 的 LayoutManager、ItemDecoration、ItemAnimator 等组件的创建和配置,类似于工厂模式。用户可以根据需求自定义这些组件,以实现不同的布局和效果。
kotlin
复制代码
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
recyclerView.itemAnimator = DefaultItemAnimator()
2. 工作原理
RecyclerView 的工作原理可以总结为以下几个步骤:
- 初始化:
RecyclerView创建Recycler和LayoutManager,并初始化Adapter和ViewHolder。 - 布局和测量:
LayoutManager负责测量和布局RecyclerView的子视图,并与Recycler协作管理视图的缓存和复用。 - 数据绑定:
Adapter负责创建和绑定ViewHolder,将数据绑定到视图上。 - 视图回收和复用:
Recycler负责管理ViewHolder的缓存,包括 Scrap 缓存和 Recycler 缓存,实现视图的高效回收和复用。 - 动画和装饰:
ItemAnimator负责处理视图的动画效果,ItemDecoration负责装饰RecyclerView的子视图,如添加分隔线。
3. 启发和代码示例
从 RecyclerView 的设计和实现中可以获得以下启发:
3.1 分离关注点
将视图的创建、数据绑定、布局管理、视图缓存等功能分离到不同的类中,每个类只负责自己的职责,使代码更加清晰和易于维护。
kotlin
复制代码
// ViewHolder 类
class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val textView: TextView = itemView.findViewById(R.id.textView)
}
// Adapter 类
class MyAdapter(private val items: List<String>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.textView.text = items[position]
}
override fun getItemCount(): Int = items.size
}
// Activity 中使用 RecyclerView
class MyActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
private lateinit var adapter: MyAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
val items = listOf("Item 1", "Item 2", "Item 3")
adapter = MyAdapter(items)
recyclerView.adapter = adapter
}
}
3.2 使用缓存提高性能
通过缓存和复用视图提高性能。RecyclerView 的 Recycler 类通过 Scrap 缓存和 Recycler 缓存管理 ViewHolder,避免重复创建和销毁视图。
kotlin
复制代码
class RecyclerView {
private val mRecycler = Recycler()
inner class Recycler {
private val mAttachedScrap = ArrayList<ViewHolder>()
private val mCachedViews = ArrayList<ViewHolder>()
fun getViewForPosition(position: Int): ViewHolder {
// 先从 Scrap 缓存中获取 ViewHolder
val scrapView = getScrapViewForPosition(position)
if (scrapView != null) {
return scrapView
}
// 再从 Recycler 缓存中获取 ViewHolder
for (i in mCachedViews.indices) {
val holder = mCachedViews[i]
if (holder.layoutPosition == position) {
mCachedViews.removeAt(i)
return holder
}
}
// 最后如果缓存中没有,则创建新的 ViewHolder
return createViewHolder()
}
fun recycleView(view: View) {
val holder = getChildViewHolder(view)
if (holder.isScrap) {
// 如果视图在 Scrap 缓存中,先移除
mAttachedScrap.remove(holder)
} else {
// 否则将视图放入 Recycler 缓存中
mCachedViews.add(holder)
}
}
private fun createViewHolder(): ViewHolder {
// 创建新的 ViewHolder
}
}
fun getChildViewHolder(view: View): ViewHolder {
// 获取 view 对应的 ViewHolder
}
}
3.3 提供扩展点
通过接口和抽象类提供扩展点,使得 RecyclerView 可以根据需求定制不同的布局管理、装饰和动画效果。
kotlin
复制代码
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
recyclerView.itemAnimator = DefaultItemAnimator()
4. 总结
RecyclerView 采用了多种设计模式,如 ViewHolder 模式、Adapter 模式、责任链模式和工厂模式,使其具有高效、灵活和可扩展的特点。这些设计模式的应用,不仅提高了 RecyclerView 的性能,还使其易于定制和扩展。通过学习 RecyclerView 的设计和实现,可以获得许多关于分离关注点、使用缓存和提供扩展点的启发,这些都是编写高效和可维护代码的重要原则。