在 Android 的视图系统中,requestLayout()、onLayout()、onDraw() 和 drawChild() 是视图布局(Layout)和绘制(Draw)流程中的关键方法。它们的作用和调用顺序密切相关,但职责不同。以下是它们的区别和联系:
1. requestLayout()
- 作用
请求对视图树进行重新测量(measure)和布局(layout)。当 View 的尺寸或位置发生变化(例如内容改变、子 View 动态添加/移除)时,需要调用此方法触发重新布局。 - 触发流程
调用requestLayout()后,系统会从当前 View 向上回溯到根 View(ViewRootImpl),触发整个视图树的measure()和layout()流程,最终可能导致onLayout()被调用。 - 注意
- 不会直接触发
onDraw()(重绘需要invalidate())。 - 频繁调用可能导致性能问题(如布局抖动)。
- 不会直接触发
2. onLayout()
- 作用
确定 View 及其子 View 的位置和尺寸。在布局流程中,父 View(通常是ViewGroup)通过onLayout()安排子 View 的位置。 - 触发时机
在measure()流程完成后,由layout()方法调用。 - 关键行为
- 对
ViewGroup:遍历子 View,调用每个子 View 的layout(l, t, r, b)方法,设置其位置。 - 对普通
View:默认无操作(因为单个 View 的位置由父 View 直接设置)。
- 对
3. onDraw()
- 作用
绘制 View 的视觉内容(如背景、文字、自定义图形)。通过Canvas对象实现具体绘制逻辑。 - 触发时机
在绘制流程中,由View.draw()方法调用。调用invalidate()或界面需要更新(如首次显示、动画)时会触发重绘。 - 注意
- 不要在此方法中创建对象或执行耗时操作(可能引发卡顿)。
- 若需要强制重绘,调用
invalidate()或postInvalidate()(非 UI 线程)。
4. drawChild()
- 作用
ViewGroup的方法,用于绘制子 View。在ViewGroup的绘制流程中,会遍历子 View 并调用drawChild(),最终触发子 View 的draw()方法。 - 触发时机
在ViewGroup的dispatchDraw()方法中被调用(dispatchDraw()是View.draw()的一部分)。 - 关键行为
- 处理子 View 的绘制顺序、裁剪区域、动画等。
- 默认实现直接调用子 View 的
draw()方法,但可重写以实现特殊效果(如叠加绘制内容)。
联系与流程
-
布局流程
requestLayout()→ 触发measure()→onMeasure()→layout()→onLayout()- 当 View 的尺寸或位置需要更新时调用
requestLayout(),最终触发onLayout()重新排列子 View。
- 当 View 的尺寸或位置需要更新时调用
-
绘制流程
invalidate()→ 触发draw()→onDraw()→dispatchDraw()→drawChild()onDraw()负责绘制自身内容,drawChild()负责绘制子 View。
-
协作关系
requestLayout()影响布局,可能导致重绘(因位置变化)。onLayout()确定子 View 的位置,drawChild()确保子 View 被正确绘制。onDraw()和drawChild()属于同一绘制流程,但作用对象不同(自身 vs 子 View)。
总结对比
| 方法 | 归属 | 作用 | 触发条件 |
|---|---|---|---|
requestLayout() |
View | 触发重新测量和布局 | View 尺寸/结构变化 |
onLayout() |
View/ViewGroup | 安排子 View 的位置 | 布局流程(layout() 调用) |
onDraw() |
View | 绘制自身内容 | 绘制流程(draw() 调用) |
drawChild() |
ViewGroup | 绘制单个子 View | dispatchDraw() 遍历子 View |
使用场景
-
自定义 View
- 重写
onLayout():调整子 View 的位置(如实现流式布局)。 - 重写
onDraw():实现自定义绘制(如圆形进度条)。 - 重写
drawChild():在子 View 绘制时添加额外效果(如统一阴影)。
- 重写
-
性能优化
- 避免在
onDraw()中频繁创建对象(如Paint)。 - 减少不必要的
requestLayout()调用(如批量更新属性)。
- 避免在