RecyclerView 卡顿是 Android 开发中的常见问题,通常由布局复杂、主线程阻塞或资源管理不当导致。以下是系统化的优化策略,结合代码示例和场景分析:
⚙️ 一、布局优化(减少测量/绘制开销)
-
简化 Item 布局层级
-
使用
ConstraintLayout
替代多层嵌套(如LinearLayout
嵌套),减少视图树深度。 -
示例:
xml<androidx.constraintlayout.widget.ConstraintLayout> <ImageView ... /> <TextView ... /> </androidx.constraintlayout.widget.ConstraintLayout>
-
-
固定尺寸与复用
- 若 Item 高度固定,调用
recyclerView.setHasFixedSize(true)
避免频繁重新测量。 - 避免在布局中使用
wrap_content
(尤其是复杂视图),改用固定尺寸或match_parent
。
- 若 Item 高度固定,调用
🧩 二、数据绑定与更新优化
-
使用 DiffUtil 局部更新
-
替代
notifyDataSetChanged()
,仅刷新变化的 Item,减少无效重绘。 -
示例:
inival diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList)) diffResult.dispatchUpdatesTo(adapter)
-
-
避免耗时操作在 onBindViewHolder
-
禁止在
onBindViewHolder
中执行网络请求、复杂计算或频繁创建对象。数据预处理应在后台线程完成。 -
图片加载使用 Glide/Picasso 并启用缓存:
scssGlide.with(holder.itemView) .load(url) .override(800, 600) // 限制尺寸 .diskCacheStrategy(DiskCacheStrategy.ALL) .into(holder.imageView)
-
⚡️ 三、滚动性能优化
-
预加载与缓存
- 增加 Item 缓存数量:
recyclerView.setItemViewCacheSize(20)
。 - 预加载下一页数据(如 Paging 库)或自定义
LayoutManager
重写calculateExtraLayoutSpace()
。
- 增加 Item 缓存数量:
-
滑动时暂停非关键操作
-
监听滚动状态,滑动时暂停图片加载:
kotlinrecyclerView.addOnScrollListener(object : OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { if (newState == SCROLL_STATE_DRAGGING) { Glide.with(context).pauseRequests() } else { Glide.with(context).resumeRequests() } } })
-
📦 四、资源管理与内存优化
-
ViewHolder 复用
- 确保 ViewHolder 仅通过
findViewById
初始化一次视图,避免重复查找。 - 复杂视图(如折叠文本)使用
ViewStub
延迟加载。
- 确保 ViewHolder 仅通过
-
共享 RecycledViewPool
-
多个同类型 RecyclerView 共享池:
scssval sharedPool = RecyclerView.RecycledViewPool() recyclerView1.setRecycledViewPool(sharedPool) recyclerView2.setRecycledViewPool(sharedPool)
-
-
及时释放资源
- 在
onViewRecycled()
中取消图片加载或清除引用。
- 在
🚀 五、高级技巧
-
分页加载大数据集
- 使用 Android Paging 库分批加载数据,避免一次性渲染千条项目。
-
减少动画开销
- 禁用默认动画:
recyclerView.itemAnimator = null
。
- 禁用默认动画:
-
多类型 Item 优化
- 使用
ConcatAdapter
合并多个 Adapter,独立复用各类型 ViewHolder。
- 使用
💎 快速检查清单
问题类型 | 优化动作 |
---|---|
滚动卡顿 | 启用预加载、增加缓存大小、滑动时暂停图片加载 |
数据更新导致卡顿 | 使用 DiffUtil 替代全局刷新、避免 onBindViewHolder 耗时操作 |
内存占用过高 | 分页加载、复用 ViewHolder、释放无用资源 |
布局渲染慢 | 简化 Item 布局、固定尺寸、避免 wrap_content |
通过组合上述策略(如电商列表优化后 FPS 从 <30 提升至 50-60),可显著提升流畅度。若需深入特定场景(如视频流或嵌套滚动),可进一步分析源码或 Profiler 数据定位瓶颈。