一句话总结:
优化自定义 View 的最高境界,是首先思考“我能否不写自定义 View?”。对于必须手写的场景,要精通传统 View 的优化“手艺”;而面向未来,则应拥抱 Jetpack Compose 这一“智能拼装”的新范式,它在架构层面就为我们解决了大部分性能问题。
第一章:“匠人”的技艺——精通传统自定义 View 的优化
当我们需要维护存量代码或必须使用 View 体系时,掌握一套精湛的“装修”手艺至关重要。
核心原则一:为 onDraw() 极致“减负”
onDraw 是渲染的“热路径”,每秒可能被调用 60 次。它的任何性能抖动都会直接体现为卡顿。
- 零对象分配: 严禁在
onDraw方法内new任何对象。Paint,Rect,Path等所有绘图相关的对象,都必须在View初始化时创建并复用。 - 零耗时操作: 严禁在
onDraw方法内执行任何耗时操作,如解码Bitmap、文件 I/O 等。所有数据和资源的准备,都应提前到onMeasure、onLayout甚至构造函数中。
核心原则二:避免不必要的“粉刷”——对抗过度绘制
- 裁剪绘制区域 (
canvas.clipRect): 如果你只需要重绘View的一小部分,请使用clipRect告诉Canvas:“只在脏区内作画!” - 移除冗余背景: 检查
View和其父容器,确保没有被完全覆盖的背景仍在被绘制。
核心原则三:理解硬件加速的“交易”
setLayerType(View.LAYER_TYPE_HARDWARE, null) 并非免费的性能“开关”。它是在用**“更多的内存”去交换“更快的动画/绘制”**。
- 何时使用? 当你的
View包含大量复杂的绘制操作(如多条Path、复杂的 Shader),或者需要频繁地进行alpha/rotation/scale动画时,开启硬件层可以带来巨大收益。 - 何时避免? 对于简单的
View,开启它反而会增加不必要的内存开销。
第二章:工程师的思考——我真的需要一个“自定义 View”吗?
在继承 View 开始码字之前,请先自问:
-
我想要的是一个自定义的“布局容器”吗?
- 如果是,99% 的场景下
ConstraintLayout都是更优的选择。它能用扁平的层级实现极其复杂的布局,避免了自定义ViewGroup中繁琐的onMeasure/onLayout逻辑。
- 如果是,99% 的场景下
-
我想要的只是一个自定义的“外观”吗?
- 如果是,优先考虑创建一个自定义
Drawable。你可以实现复杂的绘图逻辑,然后将其应用到任何ImageView或View的背景上。这比创建一个完整的View子类更轻量、更解耦。
- 如果是,优先考虑创建一个自定义
第三章:“未来”的工艺——用 Jetpack Compose 进行“智能拼装”
Jetpack Compose 彻底改变了游戏规则。它用**“组合”替代了“继承” ,用“声明式”替代了“命令式”**。
架构性地解决旧问题
-
onDraw 内存抖动?不存在了。
在 Compose 中,状态对象通过 remember { ... } 创建,其生命周期由 Compose 运行时管理,天然避免了在绘制阶段重复创建。
val paint = remember { Paint().apply { color = Color.RED } } Canvas(modifier = Modifier.fillMaxSize()) { drawCircle(color = Color.Red, radius = 50f) // 直接使用状态 } -
invalidate() 和 requestLayout()?被淘汰了。
UI 由“状态”驱动。当状态改变时,Compose 的**“智能重组”**机制会自动找出需要更新的最小 UI 单元并重绘。开发者不再需要手动管理刷新。
从“手绘”到“拼装”
过去我们需要写一个复杂的自定义 View 来画一个带图标、标题和子标题的卡片。在 Compose 中,我们只是简单地“拼装”:
@Composable
fun MyCustomCard(icon: ImageVector, title: String, subtitle: String) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(imageVector = icon, contentDescription = null)
Column {
Text(text = title, fontWeight = FontWeight.Bold)
Text(text = subtitle, style = MaterialTheme.typography.body2)
}
}
}
这不仅代码更少、更直观,其底层的布局和绘制也经过了现代化的高度优化。
四、总结:你的自定义 UI 决策路径
| 需求 | 首选方案 (现代) | 备用方案 (传统) |
|---|---|---|
| 构建全新的 UI 界面 | Jetpack Compose | (不推荐) |
| 实现一个复杂的布局 | Jetpack Compose | ConstraintLayout |
| 实现一个复杂的静态外观 | 在 Compose 中使用 Canvas | 自定义 Drawable |
维护旧的自定义 View 代码 | (重构为 Compose) | 遵循第一章的“匠人”优化法则 |
最终建议:
精通传统 View 的优化技巧,是你作为一名资深 Android 开发者的“基本功”,它能让你在任何项目中游刃有余。而拥抱 Jetpack Compose,则是让你跳出“手工作坊”的思维局限,用更先进、更高效的“工业化”思想去构建未来的用户界面。