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 文件inflateItem 视图。 - 创建并返回一个
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 |