1. 使用流程对比
-
ListView:
- 布局XML: 在布局文件中放置
<ListView>
控件,指定id
(如android:id="@+id/listView"
)。 - 数据适配器 (Adapter): 继承
BaseAdapter
或ArrayAdapter
/CursorAdapter
/SimpleAdapter
。- 重写
getCount()
:返回数据项总数。 - 重写
getItem(int position)
:返回指定位置的数据对象。 - 重写
getItemId(int position)
:返回指定位置项的 ID(通常就是position
)。 - 核心:重写
getView(int position, View convertView, ViewGroup parent)
:- 检查
convertView
是否为空(是否有可复用的视图)。为空则通过LayoutInflater
从布局 XML 文件inflate
一个新视图。 - 查找视图中的子控件(如
TextView
,ImageView
)。 - 根据
position
获取数据对象。 - 将数据对象绑定到子控件上。
- 手动处理视图复用:
convertView
机制是实现复用的关键,开发者需要自己管理。
- 检查
- 重写
- 设置适配器: 在 Activity/Fragment 中,
findViewById
获取ListView
实例,调用setAdapter(adapter)
设置适配器。 - (可选) 设置监听器: 如
setOnItemClickListener
,setOnItemLongClickListener
。
- 布局XML: 在布局文件中放置
-
RecyclerView:
- 布局XML: 在布局文件中放置
<androidx.recyclerview.widget.RecyclerView>
控件,指定id
(如android:id="@+id/recyclerView"
)。 - ViewHolder 模式 (强制): 创建一个继承自
RecyclerView.ViewHolder
的内部类。- 在构造方法中
findViewById
,持有 Item 布局中子控件的引用。
- 在构造方法中
- 适配器 (Adapter): 继承
RecyclerView.Adapter<YourViewHolder>
。- 重写
onCreateViewHolder(ViewGroup parent, int viewType)
:- 使用
LayoutInflater
从布局 XML 文件inflate
Item 视图。 - 创建并返回一个
YourViewHolder
实例(传入刚 inflate 的视图)。
- 使用
- 重写
onBindViewHolder(YourViewHolder holder, int position)
:- 根据
position
获取数据对象。 - 通过
holder
对象访问其持有的子控件引用。 - 将数据对象绑定到这些子控件上。
- 根据
- 重写
getItemCount()
:返回数据项总数。 - (可选) 重写
getItemViewType(int position)
: 用于处理多种类型的 Item。
- 重写
- 设置布局管理器 (LayoutManager - 必需):
RecyclerView
必须 设置一个LayoutManager
。- 常用实现:
LinearLayoutManager
: 线性列表(垂直或水平)。GridLayoutManager
: 网格布局。StaggeredGridLayoutManager
: 瀑布流布局。
- 代码:
recyclerView.setLayoutManager(new LinearLayoutManager(context));
- 设置适配器:
recyclerView.setAdapter(yourAdapter);
- (可选) 设置 Item 装饰 (ItemDecoration) 和 Item 动画 (ItemAnimator): 提供分割线、间隔、增删改动画等。
- (可选) 处理点击事件:
RecyclerView
没有内置OnItemClickListener
。需要在ViewHolder
的构造函数中或在onBindViewHolder
里为 Item 视图或其子控件设置点击监听 (setOnClickListener
)。
- 布局XML: 在布局文件中放置
流程关键差异:
- ViewHolder 强制化:
RecyclerView
强制使用 ViewHolder 模式,将findViewById
的开销从频繁调用的onBindViewHolder
移到了只调用几次的onCreateViewHolder
中,显著提升性能。 - 布局分离:
RecyclerView
通过LayoutManager
将布局策略(线性、网格、瀑布流)完全解耦,无需为不同布局重写整个适配器。 - 事件处理:
ListView
提供内置 Item 点击监听,RecyclerView
需要手动实现,灵活性更高(可以监听 Item 内任意子控件的点击)。 - 复用机制:
ListView
的复用 (convertView
) 需要开发者在getView
中手动管理。RecyclerView
的复用由系统通过Adapter
(onCreateViewHolder
/onBindViewHolder
) 和LayoutManager
自动处理,开发者只需遵循 ViewHolder 模式。
2. 应用场景对比
-
ListView (当前适用场景非常有限):
- 维护非常老旧的 Android 项目(API level < 21 或未引入支持库)。
- 需要实现极其简单 、静态 、单类型 且性能要求不高的短列表。
- 不推荐在新项目中使用。
-
RecyclerView (现代首选):
- 绝大多数列表/网格/瀑布流需求。
- 需要复杂布局(多种 Item 类型)。
- 需要高度定制化的布局(如水平滑动列表、网格、交错网格、自定义排列)。
- 需要精细控制项目动画(增、删、改、移动)。
- 需要添加项目装饰(如分割线、间隔、高亮)。
- 需要高性能 处理超长列表 或复杂 Item 布局。
- 需要局部更新 数据(
notifyItemChanged()
,notifyItemInserted()
等),避免全局刷新。 - 所有新项目都应使用 RecyclerView。
3. 实现原理对比
-
ListView:
- 继承自
AbsListView
。 - 核心在
Adapter
的getView()
: 系统在需要显示 Item 时调用此方法。position
指明位置,convertView
是可能可复用的旧视图(由系统管理一个复用池 -RecycleBin
),parent
是ListView
本身。 - 复用池 (
RecycleBin
):ListView
内部维护一个有限的视图复用池。当 Item 滚出屏幕时,其视图可能被放入池中。当需要新的 Item 视图时,系统尝试从池中取 (getScrapView()
) 一个同类型的视图 (convertView
) 给getView()
复用。开发者负责在getView()
中检查convertView
并重置内容。 - 布局:
ListView
自身负责垂直(或水平)堆叠排列子视图。不支持网格或瀑布流(除非自定义或使用GridView
,但GridView
也有类似限制)。 - 测量与布局: 在
onMeasure
和onLayout
中,ListView
会遍历所有需要显示的 Item(或预估),调用它们的measure
和layout
。
- 继承自
-
RecyclerView:
- 核心设计哲学:关注点分离 (Separation of Concerns)
Adapter
: 负责提供数据 (getItemCount
,getItem
) 和创建/绑定ViewHolder
(onCreateViewHolder
,onBindViewHolder
)。ViewHolder
: 持有 Item 视图及其子控件的引用,避免重复findViewById
。LayoutManager
: 核心创新点! 完全负责 Item 的测量 和布局 。决定 Item 在屏幕上的摆放位置(线性、网格、瀑布流、自定义)。它管理着视图的附加/分离和复用策略。RecyclerView
本身不知道如何布局。Recycler
: 由LayoutManager
使用。LayoutManager
在需要视图时会向Recycler
请求 (getViewForPosition
)。Recycler
管理着多级缓存池 (Scrap, Cache, ViewCacheExtension, RecycledViewPool),优先从缓存中提供视图。如果缓存中没有,则要求Adapter
创建新的ViewHolder
(onCreateViewHolder
)。ItemAnimator
: 负责 Item 的增、删、改、移动动画。ItemDecoration
: 负责在 Item 周围绘制装饰(分割线、间隔、边框等),不影响 Item 的测量和布局。
- 工作流程简述:
RecyclerView
被测量和布局时,将任务委托给LayoutManager
。LayoutManager
开始遍历需要显示的位置。- 对于每个位置,
LayoutManager
向Recycler
请求该位置的视图 (getViewForPosition
)。 Recycler
检查各级缓存:- Scrap: 当前布局过程中临时分离但很快会重新附加的视图(如正在滚出但尚未完全离开屏幕的 Item)。
- Cache: 刚刚滚出屏幕的视图(
mAttachedScrap
和一级缓存)。类型匹配可直接复用。 - ViewCacheExtension (可选): 开发者自定义的缓存层。
- RecycledViewPool: 最终的共享池。存放被完全移除且类型相同的视图。
onBindViewHolder
会被重新调用。
- 如果缓存中找到视图,
Recycler
返回它(可能需要重新绑定数据)。 - 如果缓存中没有,
Recycler
要求Adapter
创建新的ViewHolder
(onCreateViewHolder
)。 LayoutManager
将获取到的视图添加到RecyclerView
中并测量、布局它。- 当 Item 滚出屏幕,
LayoutManager
将其视图回收到Recycler
的缓存中(通常是 Cache 或 RecycledViewPool)。
- 核心设计哲学:关注点分离 (Separation of Concerns)
原理关键差异:
- 架构:
ListView
是相对单一的整体。RecyclerView
是高度模块化的设计(Adapter, ViewHolder, LayoutManager, ItemAnimator, ItemDecoration, Recycler),职责清晰分离,扩展性极强。 - 布局控制:
ListView
自身处理布局。RecyclerView
将布局职责完全委托给可插拔的LayoutManager
,这是实现多样化布局(网格、瀑布流)的基础。 - 复用机制:
ListView
使用相对简单的两级复用池 (ActiveView
+ScrapView
)。RecyclerView
使用更精细、可扩展的四级缓存 机制 (Scrap
+Cache
+ViewCacheExtension
+RecycledViewPool
),并由LayoutManager
和Recycler
紧密协作管理,效率更高,尤其对复杂布局和多类型 Item。 - ViewHolder 强制化:
RecyclerView
将ListView
中推荐的最佳实践 (ViewHolder) 变为强制要求,从架构上保证了性能优化的基础。
4. 缓存机制对比
-
ListView (RecycleBin):
ActiveView
: 当前屏幕上完全可见的 Item 视图。这些视图是"活跃"的。ScrapView
: 刚刚滚出屏幕的 Item 视图(通常存储在mScrapViews
或mRecycler
中)。当新 Item 需要进入屏幕时,系统优先尝试从ScrapView
中获取同类型的视图作为convertView
给getView()
复用。如果ScrapView
中没有匹配的,可能会新建视图。ScrapView
是 ListView 主要的复用来源。- 特点:
- 两级缓存(Active + Scrap)。
- 缓存以 Item 位置 (Position) 为主要标识(虽然也看类型
itemType
,但不如RecyclerView
严格)。 - 复用发生在
getView()
方法内部,开发者手动处理convertView
。 - 没有不同
ListView
实例间的视图共享机制。
-
RecyclerView (Recycler):
- Scrap (Attached Scrap & Changed Scrap):
Attached Scrap
: 在布局过程中(如onLayout
)临时从父视图分离但不需要重新绑定 的视图。通常是因为布局调整(如滚动一点距离)暂时移出但很快会放回的视图。不需要调用onBindViewHolder
。Changed Scrap
: 在布局过程中被标记为需要更新的视图(调用了notifyItemChanged
)。它们会被优先复用,并且复用时会调用onBindViewHolder
(带 payloads 如果有的话)。
- Cache (View Cache):
- 刚刚滚出屏幕的视图。它们被保留在内存中,类型匹配 且位置在屏幕附近 。当用户反向滚动时,可以非常快地重新附加,且不需要重新绑定数据 (
onBindViewHolder
不会被调用),因为数据假设未变。这是RecyclerView
流畅滚动体验的关键之一。缓存大小通常有限(默认 2 个)。
- 刚刚滚出屏幕的视图。它们被保留在内存中,类型匹配 且位置在屏幕附近 。当用户反向滚动时,可以非常快地重新附加,且不需要重新绑定数据 (
- ViewCacheExtension (可选 - 开发者扩展):
- 开发者可以继承
ViewCacheExtension
实现自定义的缓存层。可以存储特定类型的视图或应用特殊逻辑。较少使用。
- 开发者可以继承
- RecycledViewPool (回收视图池):
- 核心优势点! 存储的是完全移除 (既不在屏幕也不在 Cache/Scrap 中)且按类型 (
viewType
) 分类的视图 (ViewHolder
)。 - 当
LayoutManager
请求一个视图,且 Scrap/Cache/Extension 都没有时,会到RecycledViewPool
中查找相同viewType
的视图。 - 如果找到,该视图会被返回给
Adapter
进行数据重新绑定 (调用onBindViewHolder
)。 - 如果
RecycledViewPool
中也没有,则Adapter
会创建新的ViewHolder
(onCreateViewHolder
)。 - 关键特性:
- 按
viewType
存储: 严格区分不同类型 Item 的视图。 - 可跨
RecyclerView
实例共享: 多个RecyclerView
实例(即使是不同列表)可以设置同一个RecycledViewPool
(setRecycledViewPool
)。这对于具有相同 Item 类型的多个列表(如标签选择器、多 Tab 下的同类型列表)是巨大的性能优化,避免了重复创建视图的开销。 - 池大小可配置: 可以为每种
viewType
设置最大缓存数量 (setMaxRecycledViews
)。
- 按
- 核心优势点! 存储的是完全移除 (既不在屏幕也不在 Cache/Scrap 中)且按类型 (
- Scrap (Attached Scrap & Changed Scrap):
缓存机制关键差异:
- 层级与精细度:
RecyclerView
拥有更复杂、更精细的四级缓存(特别是分离的 Scrap 和 Cache),且严格按viewType
区分,复用更精准高效。 - Cache 层:
RecyclerView
独有的 Cache 层避免了附近 Item 滚回时昂贵的onBindViewHolder
调用,极大提升回滚流畅度。 - RecycledViewPool:
RecyclerView
独有的RecycledViewPool
实现了跨列表的视图复用,是ListView
完全不具备的能力,对复杂 UI 优化意义重大。 - 位置 vs 类型:
ListView
缓存更依赖位置信息,RecyclerView
缓存则强依赖viewType
,后者在数据动态变化(增删)时更健壮。 - 管理方式:
ListView
缓存复用逻辑需要开发者在getView
中参与(检查convertView
)。RecyclerView
的缓存完全由系统 (Recycler
+LayoutManager
) 自动管理,开发者只需遵循 ViewHolder 模式。
5. 优化方案对比
-
ListView 优化 (本质是优化
getView()
):- 利用
convertView
: 这是最重要的优化!务必检查convertView != null
并复用,避免不必要的inflate
。 - 应用 ViewHolder 模式 (非强制但必须做): 在
convertView
的tag
中存储子控件引用 (setTag
/getTag
),避免每次findViewById
。这是RecyclerView
强制化的原因。 - 减少 Item 布局层次和复杂度: 使用
Hierarchy Viewer
或Layout Inspector
分析,避免嵌套过深。使用ConstraintLayout
替代多层嵌套的LinearLayout
/RelativeLayout
。 - 图片加载优化: 使用
Picasso
,Glide
或Coil
等库异步加载、缓存和正确处理图片回收。 - 避免在
getView()
中做耗时操作: 如网络请求、复杂计算、频繁 I/O。只做数据绑定。 - 分批加载/分页: 对于超长列表,实现滚动到底部加载更多数据。
- 使用
android:scrollingCache="false"
(谨慎): 禁用滚动时的颜色缓存,可能略微提升滚动性能(但可能牺牲视觉平滑度)。需测试效果。 - 使用
android:fastScrollEnabled="true"
(仅视觉): 启用快速滚动滑块,方便用户快速导航长列表。
- 利用
-
RecyclerView 优化 (充分利用其架构优势):
- 遵循 ViewHolder 模式: 这是基础,已在架构中保证。
- 合理使用
notify
方法族: 绝对避免 在数据变化时总是调用notifyDataSetChanged()
!使用精细化的方法:notifyItemInserted(position)
notifyItemRemoved(position)
notifyItemMoved(fromPosition, toPosition)
notifyItemChanged(position)
notifyItemRangeInserted(positionStart, itemCount)
等。- 使用 Payloads: 当 Item 只有部分内容变化时,在
notifyItemChanged(position, payload)
中传递变化的 payload 对象。在Adapter
的onBindViewHolder(VH holder, int position, List<Object> payloads)
中根据payloads
进行增量更新 ,避免重绘整个 Item。这是RecyclerView
独有的高级优化。
- 优化
onCreateViewHolder
和onBindViewHolder
:onCreateViewHolder
:尽量高效,只做inflate
和创建ViewHolder
。避免耗时操作。onBindViewHolder
:只做数据绑定。避免在此创建新对象、做耗时操作。利用 payloads 进行局部更新。
- 减少 Item 布局层次和复杂度: 同
ListView
优化 3。使用高效布局和ConstraintLayout
。 - 图片加载优化: 同
ListView
优化 4。库通常能很好配合RecyclerView
。 - 预加载 (Prefetching):
RecyclerView
内置了预取机制(默认开启)。LayoutManager
会在空闲时间预取即将进入屏幕的 Item 视图。通常不需要手动干预。确保LayoutManager
支持(LinearLayoutManager
/GridLayoutManager
支持)。 - 配置
RecycledViewPool
:- 对于同类型 Item 的多个列表 ,共享 同一个
RecycledViewPool
(setRecycledViewPool
) 是极其重要的优化。 - 根据应用场景和 Item 类型内存占用,调整每种
viewType
的缓存池大小 (setMaxRecycledViews
)。避免过大(内存浪费)或过小(频繁创建 ViewHolder)。
- 对于同类型 Item 的多个列表 ,共享 同一个
- 使用
setItemViewCacheSize(int size)
: 适当增大 Cache 层的大小(默认通常是 2)。增大它可以让更多刚滚出屏幕的 Item 视图保留在 Cache 中,提升回滚性能(避免重新绑定)。但会增加内存占用。需根据 Item 复杂度和设备内存平衡。 - 使用
setHasFixedSize(true)
: 如果RecyclerView
自身的大小不会随 Adapter 内容的变化而改变(即宽高固定或match_parent
),调用此方法可以跳过不必要的自身测量步骤,优化性能。 - 合理使用 DiffUtil: 当数据集发生复杂变化(多个增删改操作混合)时,使用
DiffUtil
类计算新旧数据集差异,并自动调用最合适的精细notify...
方法。比手动计算和调用更高效且不易出错。尤其适合配合Paging
库或后台数据更新。 - 选择高效的
LayoutManager
: 对于超大数据集或复杂 Item,LinearLayoutManager
通常是最优解。StaggeredGridLayoutManager
在布局计算上可能稍重。 - 优化 Item 动画: 如果不需要默认动画,设置
DefaultItemAnimator
为null
(setItemAnimator(null)
)。自定义复杂动画也可能影响性能。
优化关键差异:
- 精细化更新:
RecyclerView
通过notifyItem...
系列方法和DiffUtil
支持局部更新,是ListView
的notifyDataSetChanged()
无法比拟的。 - Payloads:
RecyclerView
独有的增量更新机制,对复杂 Item 优化效果显著。 - 缓存配置:
RecyclerView
提供setItemViewCacheSize
和RecycledViewPool
的精细控制(包括跨列表共享),优化手段更丰富。 - 预加载:
RecyclerView
内置预取机制提升流畅度。 - 固定尺寸优化:
setHasFixedSize(true)
是RecyclerView
特有的优化点。 - ViewHolder 基础:
ListView
优化需手动实现 ViewHolder,RecyclerView
已强制使用。
总结
特性 | ListView | RecyclerView | 结论与优势 |
---|---|---|---|
年代/状态 | 较老,基本被弃用 | 现代,官方推荐,持续更新 | RecyclerView 是未来 |
架构 | 相对单一 | 高度模块化 (Adapter, VH, LayoutManager, Animator, Decoration, Recycler) | RecyclerView 更灵活、可扩展、职责清晰 |
布局能力 | 仅垂直/水平线性列表 | 通过 LayoutManager 支持线性、网格、瀑布流及任意自定义布局 | RecyclerView 布局能力碾压 |
ViewHolder | 推荐使用但非强制 | 强制使用 | RecyclerView 从架构保证性能基础 |
使用流程 | getView() 中手动处理 convertView 和 findViewById |
分离 onCreateViewHolder (创建/找控件) 和 onBindViewHolder (绑定数据) |
RecyclerView 代码更清晰,性能更好 (findViewById 开销低) |
缓存机制 | 两级 (Active + Scrap),按位置为主 | 四级缓存 (Scrap, Cache, Extension, Pool),严格按 viewType | RecyclerView 缓存更精细、高效 |
缓存复用 | 无跨列表共享 | RecycledViewPool 支持跨列表共享同类型视图 | RecyclerView 对多列表场景优化显著 |
数据更新 | 主要 notifyDataSetChanged() (全局刷新) |
精细 notifyItem...() 系列方法 + DiffUtil |
RecyclerView 局部更新效率极高 |
增量更新 | 不支持 | 支持 Payloads (部分绑定) | RecyclerView 优化复杂 Item 更新的利器 |
Item 动画 | 内置简单动画 | 强大且可定制的 ItemAnimator | RecyclerView 动画效果丰富灵活 |
装饰 | 需自定义或第三方库实现分割线等 | 内置 ItemDecoration 机制 | RecyclerView 添加装饰更标准方便 |
点击事件 | 内置 OnItemClickListener 等 |
需在 ViewHolder 或 Adapter 中手动实现 | ListView 简单,RecyclerView 更灵活 (监听子控件) |
优化点 | 优化 getView() (复用, ViewHolder, 布局扁平化) |
优化 onBindViewHolder , 使用精细 notify, Payloads, DiffUtil, 配置缓存池/大小, 预加载, 共享 Pool, setHasFixedSize |
RecyclerView 提供更多、更强大的内置和可配置优化手段 |
应用场景 | 维护旧代码,极简单短列表 | 所有现代列表/网格/瀑布流需求,复杂布局,高性能长列表 | 新项目无脑选 RecyclerView |