Android View绘制流程:Measure、Layout、Draw的深度解析
一、绘制流程三步走:从尺寸到画面
View 的绘制流程是一个严谨的三步过程,每个步骤都由父 View 驱动,自上而下地完成。
1. Measure(测量)
-
核心任务:计算每个 View 的期望尺寸。
-
工作方式:父
ViewGroup会为每个子View传递一个MeasureSpec,它包含了父ViewGroup对子View尺寸的限制。子View在onMeasure()方法中,根据MeasureSpec和自身的layout_params,计算并设置自己的measuredWidth和measuredHeight。 -
常见
MeasureSpec类型:EXACTLY:精确模式,尺寸已确定(如100dp)。AT_MOST:最大模式,尺寸不能超过某个值(如match_parent)。UNSPECIFIED:未指定模式,尺寸任意。
2. Layout(布局)
- 核心任务:确定每个 View 在父容器中的最终位置。
- 工作方式:父
ViewGroup在onLayout()方法中,遍历其所有子View,并调用child.layout(left, top, right, bottom)方法,为其设置最终的边界框。
3. Draw(绘制)
-
核心任务:将 View 的内容渲染到屏幕上。
-
工作方式:
View的draw()方法负责整个绘制过程。- 绘制背景、自身内容(
onDraw())、子 View(dispatchDraw())和前景。 - 在硬件加速模式下,
onDraw()会将绘制命令录制到DisplayList中,然后由独立的 RenderThread 将其渲染成图像。
- 绘制背景、自身内容(
二、绘制原理:幕后的大佬们
ViewRootImpl:总指挥。它驱动了整个Measure/Layout/Draw流程(通过performTraversals()方法),并与Choreographer合作,确保渲染与屏幕刷新同步。Choreographer:节奏大师。它在每个VSync信号到来时,安排主线程上的任务(包括绘制),以确保每帧渲染在 16.6ms 内完成。SurfaceFlinger:合成器。它将来自不同Window的Surface缓冲区进行合成,最终将画面提交给屏幕。
三、触发绘制与优化
invalidate():触发Draw流程。它会标记 View 为“脏”,并安排一次重绘。适用于 View 的内容发生变化,但尺寸和位置不变的场景。requestLayout():触发Measure、Layout和Draw流程。适用于 View 的尺寸或位置发生变化时。
优化策略:
- 减少布局嵌套:使用
ConstraintLayout等扁平化布局,可以显著减少Measure和Layout的计算时间。 - 避免过度绘制:移除不必要的背景,使用
canvas.clipRect()限制绘制区域,以减少 GPU 的渲染工作量。 - 离线绘制:对于复杂且静态的内容,可以先将其绘制到
Bitmap中,然后直接绘制Bitmap,从而减少onDraw()的执行时间。