在 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()
调用(如批量更新属性)。
- 避免在