一文带你吃透Android View绘制流程与原理详解

Android 中 View 的绘制流程是 UI 框架的核心机制,主要分为 Measure(测量)、Layout(布局)、Draw(绘制) 三个阶段。以下是详细的流程和实现原理分析:

一、绘制流程总览

View 的绘制流程由 ViewRootImpl(Android 3.0+)触发,通过 performTraversals() 方法协调以下三个阶段:

java 复制代码
// ViewRootImpl.java
private void performTraversals() {
    performMeasure();  // 测量
    performLayout();   // 布局
    performDraw();      // 绘制
}

二、Measure 阶段:确定 View 的尺寸

1. 核心方法

  • measure(int widthMeasureSpec, int heightMeasureSpec)
    由父 View 调用,触发子 View 的测量逻辑。
  • onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    自定义 View 需重写此方法,确定自身尺寸。

2. MeasureSpec

  • 组成 :32位 int 值,高 2 位表示 Mode (测量模式),低 30 位表示 Size。

  • 三种模式:

    • EXACTLY:精确尺寸(如 match_parent 或具体数值)。
    • AT_MOST:最大尺寸(如 wrap_content)。
    • UNSPECIFIED:未指定(如 ScrollView 的子 View)。

3. 流程细节

  • 父 View 通过 measureChild() 计算子 View 的 MeasureSpec。
  • ViewGroup 需递归测量所有子 View(如 LinearLayout 根据方向计算子 View 尺寸)。
  • 最终通过 setMeasuredDimension() 保存测量结果。

关键问题:

  • wrap_content 为何需要特殊处理?

    默认 onMeasure 不会处理 AT_MOST 模式,需要开发者手动设置尺寸。

三、Layout 阶段:确定 View 的位置

1. 核心方法

  • layout(int l, int t, int r, int b)
    由父 View 调用,确定子 View 的位置。
  • onLayout(boolean changed, int l, int t, int r, int b)
    ViewGroup 需重写此方法,排列子 View(如 FrameLayout 默认堆叠子 View)。

2. 流程细节

  • 父 View 根据子 View 的测量结果,计算其左上右下坐标。
  • 调用子 View 的 layout() 方法,触发其 onLayout()(仅 ViewGroup 需要实现)。
  • 保存位置信息到 mLeft, mTop, mRight, mBottom。

常见场景:

  • RelativeLayout 需要两次测量(依赖关系复杂)。
  • 自定义 ViewGroup 需手动计算子 View 位置。

四、Draw 阶段:绘制内容

1. 核心方法

  • draw(Canvas canvas)
    总控绘制流程,内部调用以下步骤:
    1. 绘制背景 :drawBackground()
    2. 绘制自身内容 :onDraw(Canvas canvas)
    3. 绘制子 View :dispatchDraw(Canvas canvas)
    4. 绘制装饰(如滚动条) :onDrawForeground()

2. 关键点

  • onDraw() :自定义 View 在此绘制内容(如文本、图形)。
  • dispatchDraw() :ViewGroup 通过此方法分发绘制到子 View。
  • 硬件加速:Android 4.0+ 默认开启,使用 GPU 优化绘制(需注意 API 兼容性)。

优化技巧:

  • 避免在 onDraw() 中创建对象(频繁调用导致 GC)。
  • 使用 Canvas.clipRect() 减少过度绘制。

五、实现原理与底层机制

1. ViewRootImpl 与 Choreographer

  • ViewRootImpl :连接 WindowManager 和 DecorView 的桥梁,通过 performTraversals() 触发绘制。

  • Choreographer:协调 VSYNC 信号(垂直同步),确保绘制按帧率(如 60Hz)执行。

    • 收到 VSYNC 后,通过 Handler 触发 doFrame(),进而执行 performTraversals()。

2. 消息循环机制

  • UI 线程通过 Handler 处理 MessageQueue 中的绘制任务。
  • invalidate() 和 requestLayout() 会向队列插入任务,触发重绘。

3. 双缓冲与 SurfaceFlinger

  • 双缓冲:Surface 包含一个 Front Buffer(显示)和一个 Back Buffer(绘制),减少画面撕裂。
  • SurfaceFlinger:合成多个 Surface 的内容,提交给 Display 显示。

六、常见问题与优化

1. 性能瓶颈

  • 布局嵌套过深 :导致多次 Measure/Layout(可用 ConstraintLayout 优化)。
  • 过度绘制 :可通过开发者选项中的 "Show GPU Overdraw" 检测。

2. 离线绘制

  • 场景:复杂静态内容(如图表)。
  • 方案 :使用 Bitmap 或 Canvas 预渲染,直接绘制缓存。

3. 强制重绘方法

  • invalidate() :请求重绘(主线程调用)。
  • postInvalidate() :非 UI 线程调用重绘。
  • requestLayout() :触发重新测量和布局。

常用方法:

方法 作用 触发流程
invalidate() 标记脏区域,触发重绘(Draw 阶段) 只走 draw() 流程
requestLayout() 强制重新测量和布局(可能重绘) 触发 measure() → layout() → draw()

七、自定义 View 的关键点

  1. 重写 onMeasure() :正确处理 wrap_content。
  2. 避免在 onDraw() 中分配内存:防止卡顿。
  3. 支持 padding 和 margin:在测量和绘制时需考虑。

总结

View 的绘制流程是一个递归的树形遍历过程,通过 Measure → Layout → Draw 确定每个 View 的尺寸、位置和内容。理解其底层机制(如 VSYNC、双缓冲)和优化手段,是开发高性能 UI 的关键。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. 一文带你吃透HolderFragment 实现ViewModel的生命周期穿透!
  4. 一文带你吃透Android中常见的高效数据结构
  5. 一文带你吃透Android中Service的种类和启动方式
相关推荐
熊猫钓鱼>_>3 小时前
Kotlin Multiplatform for OpenHarmony 实战:为 Reaktive 实现响应式原语适配(完整版 · 含摘要目录与技术图表)
华为·kotlin·大模型·ai编程·harmonyos·适配·reaktive
五彩小白3 小时前
自回归和上下文
android
事圆则缓6 小时前
Android 多渠道打包实战:Product Flavors、签名、AAB 与发包校验
android
树下困觉眯8 小时前
View绘制流程学习--获取WindowManager的三种方式
android·系统架构·view design
vilya9 小时前
我怎么给手机 GUI Agent 做双通道感知:无障碍树为主,投屏像素兜底
android·人工智能
李游Leo9 小时前
HarmonyOS 7 InsightBoard 多形态适配实录 06:ArkUI × 多设备回归:布局抖动、监听释放与全形态验收【鸿蒙心迹】
回归·kotlin·harmonyos
点燃大海9 小时前
手机给平板当键盘?
kotlin
李子红了时10 小时前
阿狸舞台APP(安卓端手机EncFS解密+压缩包解压+视频播放)
android·音视频
事圆则缓10 小时前
Android 使用 Jenkins 实现 CI/CD,并用 SonarQube 建立质量门禁
android·ci/cd·jenkins
其实防守也摸鱼10 小时前
DeepSeek Harness 开源贡献手记:从 Issue 到 Merge 的完整旅程
android·数据库·学习·ai·oracle·自动化