共计 1644 个字符,预计需要花费 5 分钟才能阅读完成。
在开发一个新闻列表页面时,我遇到过这样的场景:当快速滑动带有视频缩略图的 RecyclerView 时,部分 item 会出现短暂闪烁——这就是频繁切换 View.VISIBLE 和View.GONE导致的典型性能问题。每次视图的显示隐藏都会触发完整的 measure/layout/draw 流程,这种粗暴的可见性切换在列表滚动时尤其耗费资源。

一、为什么需要 hide()函数
先看两组数据对比(在 Pixel 3 设备上测试):
- 使用
View.GONE切换 100 次:平均耗时 47ms/ 次 - 使用自定义
hide()方法:平均耗时 3ms/ 次
差异主要来自两者不同的底层机制:
View.GONE会强制父视图重新计算所有子 View 的位置View.INVISIBLE虽然保留布局空间,但依然会走完整的绘制流程- 自定义
hide()通过修改View.translationZ和alpha值实现视觉隐藏,不触发视图树重构
@MainThread
fun View.hideSafely() {if (Looper.myLooper() != Looper.getMainLooper()) {post { hideImpl() }
return
}
hideImpl()}
private fun View.hideImpl() {
alpha = 0f
translationZ = -100f // 确保视图沉到最底层
isClickable = false
}
二、生命周期感知的进阶实现
结合 AndroidX 生命周期组件,我们可以避免 Activity 退出时的资源泄漏:
class SafeHideViewModel : ViewModel() {private val pendingHides = mutableMapOf<View, Runnable>()
@MainThread
fun scheduleHide(view: View, delay: Long) {val task = Runnable { view.hideSafely() }
view.postDelayed(task, delay)
pendingHides[view] = task
}
override fun onCleared() {pendingHides.forEach { (view, task) ->
view.removeCallbacks(task)
}
}
}
三、性能监控与优化
1. 使用 Systrace 定位问题
执行以下命令捕获 UI 线程状态:
python systrace.py -o trace.html -a com.example.app gfx view sched
关键检查点:
- Choreographer 帧信号间隔
- RenderThread 的任务堆积
- UI 线程的锁等待时间
2. 硬件加速动画方案
view.animate()
.setDuration(300)
.alpha(0f)
.withLayer() // 启用硬件层加速
.withEndAction {translationZ = -100f}
四、避坑指南
- 绝对禁止在 onDraw 中调用 hide
-
会导致递归绘制触发
StackOverflowError -
WindowManager 移除防护
fun detachSafely(view: View) { try {(view.parent as? ViewGroup)?.removeView(view) } catch (e: IllegalArgumentException) {// 处理视图已被移除的情况} } -
内存泄漏检测配置
dependencies {debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9'}
五、延伸思考
- 对于延迟隐藏的场景,如何设计一个防竞态的
DisposableHideRequest类? - 在 Jetpack Compose 中,
DisposeEffect如何更优雅地处理隐藏逻辑?
实际项目中,通过改用 hide() 方案后,我们的列表滑动 FPS 从 45 提升到了 58。记住:性能优化不是炫技,而是要在准确理解系统原理的基础上做出合理选择。
正文完
