RecyclerView 是 Android 开发中高频使用的列表控件,但其性能问题常导致卡顿(尤其在快速滑动、大数据量场景)。本文从 布局优化 、数据绑定 、滑动处理 、图片加载 等维度,结合示例代码提供系统优化方案。
一、基础优化:减少布局计算与对象创建
1、设置固定尺寸
- 场景 :列表项尺寸固定时,避免
onMeasure()
重复计算。
kotlin
recyclerView.setHasFixedSize(true) // 若所有项尺寸一致
2、优化布局层级
- 避免嵌套 :使用
ConstraintLayout
替代多层嵌套的LinearLayout
。
xml
<!-- 示例:单层 ConstraintLayout 实现复杂布局 -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView ... app:layout_constraintLeft_toLeftOf="parent" />
<TextView ... app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
3、复用 ViewHolder
- 避免
findViewById
:使用ViewBinding
或DataBinding
。
kotlin
class MyViewHolder(private val binding: ItemLayoutBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(data: MyData) {
binding.title.text = data.title
}
}
二、数据更新优化:减少无效刷新
1、使用 DiffUtil
计算差异
- 场景 :局部更新数据,避免
notifyDataSetChanged()
全量刷新。
kotlin
class MyDiffCallback(
private val oldList: List<MyData>,
private val newList: List<MyData>
) : DiffUtil.Callback() {
override fun getOldListSize() = oldList.size
override fun getNewListSize() = newList.size
override fun areItemsTheSame(oldPos: Int, newPos: Int) =
oldList[oldPos].id == newList[newPos].id
override fun areContentsTheSame(oldPos: Int, newPos: Int) =
oldList[oldPos] == newList[newPos]
}
// 使用示例
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList))
adapter.submitList(newList)
diffResult.dispatchUpdatesTo(adapter)
2、分页加载
- 实现思路:滑动到底部时加载下一页数据,避免一次性加载全部。
kotlin
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (!recyclerView.canScrollVertically(1)) { // 到达底部
loadNextPage()
}
}
})
三、快速滑动优化:避免卡顿
1、图片加载控制
- 滑动时暂停加载(以 Glide 为例):
kotlin
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
when (newState) {
RecyclerView.SCROLL_STATE_DRAGGING ->
Glide.with(context).pauseRequests() // 停止加载
RecyclerView.SCROLL_STATE_IDLE ->
Glide.with(context).resumeRequests() // 恢复加载
}
}
})
2、预加载机制
-
提前加载可见项前后的数据 (如 ViewPager2 的
offscreenPageLimit
)。 -
calculateExtraLayoutSpace
的使用- 核心功能
-
calculateExtraLayoutSpace
是RecyclerView.LayoutManager
的方法,用于定义超出当前可视区域外的额外布局空间。 -
通过增大布局范围,RecyclerView 可以提前加载即将进入屏幕的条目,减少滑动时的即时渲染压力。
- 适用场景
- 快速滑动:用户快速滑动列表时,预加载后续条目避免白屏。
- 复杂布局:条目布局复杂(如图文混排、动态高度)时提升渲染效率。
示例代码:
kotlin// 自定义 `LinearLayoutManager` 实现预加载 class PreloadLinearLayoutManager( context: Context, orientation: Int = VERTICAL, reverseLayout: Boolean = false ) : LinearLayoutManager(context, orientation, reverseLayout) { // 定义预加载的额外空间(单位:像素) private val extraLayoutSpace = 800 override fun calculateExtraLayoutSpace( state: RecyclerView.State, extraLayoutSpace: IntArray ) { super.calculateExtraLayoutSpace(state, extraLayoutSpace) // 设置前后方向的额外布局空间 if (orientation == VERTICAL) { extraLayoutSpace[0] = this.extraLayoutSpace // 上方预加载区域 extraLayoutSpace[1] = this.extraLayoutSpace // 下方预加载区域 } else { extraLayoutSpace[0] = this.extraLayoutSpace // 左侧预加载区域 extraLayoutSpace[1] = this.extraLayoutSpace // 右侧预加载区域 } } } //应用到 RecyclerView recyclerView.layoutManager = PreloadLinearLayoutManager(context)
3、禁用动画
- 关闭默认动画(若无需动画):
kotlin
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
四、图片加载优化:内存与流畅性
1、使用高效图片库
- 推荐方案:Glide 或 Coil,自动处理内存缓存与复用。
kotlin
// Glide 示例:加载并优化图片
Glide.with(context)
.load(imageUrl)
.placeholder(R.drawable.placeholder) // 占位图
.error(R.drawable.error) // 错误图
.override(Target.SIZE_ORIGINAL) // 控制分辨率
.diskCacheStrategy(DiskCacheStrategy.ALL) // 缓存策略
.into(imageView)
2、图片压缩与裁剪
- 按控件尺寸加载:避免加载原图浪费内存。
kotlin
Glide.with(context)
.load(imageUrl)
.override(imageView.width, imageView.height) // 按控件大小加载
.into(imageView)
五、高级技巧:提升复用效率
1、共享视图池(ViewPool)
- 场景:多个 RecyclerView 显示同类项时共享复用池。
kotlin
val sharedPool = RecyclerView.RecycledViewPool()
recyclerView1.setRecycledViewPool(sharedPool)
recyclerView2.setRecycledViewPool(sharedPool)
2、类型池预加载
- 预创建 ViewHolder:减少首次渲染时的对象创建耗时。
kotlin
recyclerView.setItemViewCacheSize(20) // 增加缓存数量
六、注意事项与建议
1、避免在 onBindViewHolder
中执行耗时操作
- 如网络请求、复杂计算应异步处理。
2、内存泄漏预防
-
在
onViewRecycled
中释放资源(如取消图片加载):kotlinoverride fun onViewRecycled(holder: MyViewHolder) { Glide.with(context).clear(holder.binding.imageView) }
3、使用 RecyclerView.setRecyclerListener
监控回收状态
- 试时分析 ViewHolder 复用情况。
七、总结
优化方向 | 核心措施 |
---|---|
布局与数据绑定 | 简化布局层级、使用 DiffUtil 局部更新 |
快速滑动处理 | 滑动时暂停图片加载、禁用非必要动画 |
图片加载优化 | Glide/Coil + 压缩缓存策略 |
复用效率提升 | 共享视图池、预加载 ViewHolder |
通过布局简化、数据差异更新、滑动预加载、图片加载优化 等综合手段,可显著提升 RecyclerView 的性能。关键点总结:
- 布局层级扁平化:减少测量和布局时间。
- DiffUtil 替代全量刷新:精准更新变化项。
- 滑动状态感知加载:平衡流畅度与用户体验。
- 合理使用缓存:内存与磁盘缓存结合,减少重复请求。