Android View绘制流程:Measure、Layout、Draw的深度解析

498 阅读2分钟

Android View绘制流程:Measure、Layout、Draw的深度解析


一、绘制流程三步走:从尺寸到画面

View 的绘制流程是一个严谨的三步过程,每个步骤都由父 View 驱动,自上而下地完成。

1. Measure(测量)

  • 核心任务:计算每个 View 的期望尺寸

  • 工作方式:父 ViewGroup 会为每个子 View 传递一个 MeasureSpec,它包含了父 ViewGroup 对子 View 尺寸的限制。子 ViewonMeasure() 方法中,根据 MeasureSpec 和自身的 layout_params,计算并设置自己的 measuredWidthmeasuredHeight

  • 常见 MeasureSpec 类型

    • EXACTLY:精确模式,尺寸已确定(如 100dp)。
    • AT_MOST:最大模式,尺寸不能超过某个值(如 match_parent)。
    • UNSPECIFIED:未指定模式,尺寸任意。

2. Layout(布局)

  • 核心任务:确定每个 View 在父容器中的最终位置
  • 工作方式:父 ViewGrouponLayout() 方法中,遍历其所有子 View,并调用 child.layout(left, top, right, bottom) 方法,为其设置最终的边界框。

3. Draw(绘制)

  • 核心任务:将 View 的内容渲染到屏幕上。

  • 工作方式Viewdraw() 方法负责整个绘制过程。

    • 绘制背景、自身内容(onDraw())、子 View(dispatchDraw())和前景。
    • 在硬件加速模式下,onDraw() 会将绘制命令录制DisplayList 中,然后由独立的 RenderThread 将其渲染成图像。

二、绘制原理:幕后的大佬们

  • ViewRootImpl总指挥。它驱动了整个 Measure/Layout/Draw 流程(通过 performTraversals() 方法),并与 Choreographer 合作,确保渲染与屏幕刷新同步。
  • Choreographer节奏大师。它在每个 VSync 信号到来时,安排主线程上的任务(包括绘制),以确保每帧渲染在 16.6ms 内完成。
  • SurfaceFlinger合成器。它将来自不同 WindowSurface 缓冲区进行合成,最终将画面提交给屏幕。

三、触发绘制与优化

  • invalidate() :触发 Draw 流程。它会标记 View 为“脏”,并安排一次重绘。适用于 View 的内容发生变化,但尺寸和位置不变的场景。
  • requestLayout() :触发 MeasureLayoutDraw 流程。适用于 View 的尺寸或位置发生变化时。

优化策略

  • 减少布局嵌套:使用 ConstraintLayout 等扁平化布局,可以显著减少 MeasureLayout 的计算时间。
  • 避免过度绘制:移除不必要的背景,使用 canvas.clipRect() 限制绘制区域,以减少 GPU 的渲染工作量。
  • 离线绘制:对于复杂且静态的内容,可以先将其绘制到 Bitmap 中,然后直接绘制 Bitmap,从而减少 onDraw() 的执行时间。