一句话说透Android里面的View的绘制流程和实现原理

一句话总结
View 的绘制流程就像盖房子:先量尺寸(Measure)→ 再摆位置(Layout)→ 最后刷墙装修(Draw)。整个过程由"包工头" ViewRootImpl 指挥,保证按时(16ms/帧)完工!


一、绘制流程三步走

1. Measure(量尺寸)

  • 核心任务:确定每个 View 的宽高。

  • 规则传递 :父 View 根据自身规则(如 LinearLayout 的权重)向子 View 传递 MeasureSpec(测量规格),包含:

    • EXACTLY :精确尺寸(如 100dp)。
    • AT_MOST :最大尺寸(如 match_parent)。
    • UNSPECIFIED :随便你(如 wrap_content,但实际可能受父容器限制)。
  • 代码入口View.measure()onMeasure()

示例

arduino 复制代码
// 自定义 View 重写 onMeasure  
@Override  
protected void onMeasure(int widthSpec, int heightSpec) {  
    int width = calculateWidth(widthSpec); // 根据父容器的限制计算自己的宽  
    int height = calculateHeight(heightSpec);  
    setMeasuredDimension(width, height); // 必须调用!  
}  

2. Layout(摆位置)

  • 核心任务:确定 View 在屏幕上的位置(四个坐标:left, top, right, bottom)。
  • 父控件的责任 :遍历子 View,调用 child.layout() 设置其位置。
  • 代码入口View.layout()onLayout()

示例

java 复制代码
// 自定义 ViewGroup 重写 onLayout  
@Override  
protected void onLayout(boolean changed, int l, int t, int r, int b) {  
    for (int i = 0; i < getChildCount(); i++) {  
        View child = getChildAt(i);  
        child.layout(childLeft, childTop, childRight, childBottom);  
    }  
}  

3. Draw(刷墙装修)

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

  • 绘制顺序

    1. 绘制背景(drawBackground())。
    2. 绘制自身内容(onDraw())。
    3. 绘制子 View(dispatchDraw())。
    4. 绘制装饰(如滚动条、前景)。
  • 代码入口View.draw()onDraw()

示例

typescript 复制代码
// 自定义 View 重写 onDraw  
@Override  
protected void onDraw(Canvas canvas) {  
    super.onDraw(canvas);  
    canvas.drawCircle(x, y, radius, paint); // 画个圆  
}  

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

1. ViewRootImpl:总包工头

  • 职责

    • 管理整个 View 树的绘制流程(performTraversals())。
    • 通过 Choreographer 监听 VSync 信号(屏幕刷新脉冲),确保每帧在 16ms 内完成。

2. Choreographer:节奏大师

  • 作用:协调输入、动画、绘制事件,保证按 VSync 信号同步执行。
  • 卡顿检测:如果一帧超时(>16ms),触发掉帧(Jank)。

3. SurfaceFlinger:刷墙队

  • 职责:将各窗口(Surface)的绘制结果合成,最终显示到屏幕。
  • 双缓冲机制:避免绘制过程中出现闪烁(Back Buffer 绘制,Front Buffer 显示)。

三、触发绘制的两种方式

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

示例

scss 复制代码
// 点击按钮后更新 View  
button.setOnClickListener {  
    myView.updateContent(); // 修改数据  
    myView.invalidate();    // 触发重绘  
}  

四、优化绘制的实战技巧

1. 减少层级嵌套

  • 问题:嵌套过深导致多次测量(如 RelativeLayout 嵌套 LinearLayout)。
  • 解决 :使用 ConstraintLayout 替代,扁平化布局。

2. 避免过度绘制

  • 检测工具:开发者选项 → "显示过度绘制区域"。

  • 优化方案

    • 移除不必要的背景(如父容器已设背景,子 View 无需重复)。
    • 使用 canvas.clipRect() 限制绘制区域。

3. 离线绘制

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

五、总结口诀

  • 绘制流程三步走,测量布局再刷墙
  • ViewRoot 总指挥,Choreographer 卡节奏
  • invalidate 重绘,requestLayout 全量走
  • 优化层级少嵌套,流畅体验不用愁!
相关推荐
遇见你的那天35 分钟前
反编译查看源码
android
用户2018792831671 小时前
SIGABRT+GL errors Native Crash 问题分析
android
Nathan202406161 小时前
Kotlin-Sealed与Open的使用
android·前端·面试
2501_916013741 小时前
iOS 26 设备文件管理实战指南,文件访问、沙盒导出、系统变更与 uni-app 项目适配
android·ios·小程序·uni-app·cocoa·iphone·webview
2501_915921431 小时前
前端用什么开发工具?常用前端开发工具推荐与不同阶段的选择指南
android·前端·ios·小程序·uni-app·iphone·webview
2501_916007471 小时前
iOS 26 能耗检测实战指南,升级后电池掉速是否正常 + KeyMob + Instruments 实时监控 + 优化策略
android·macos·ios·小程序·uni-app·cocoa·iphone
2501_916013742 小时前
苹果上架 App 全流程详解,iOS 应用发布步骤、ipa 文件上传工具、TestFlight 测试与 App Store 审核经验
android·ios·小程序·https·uni-app·iphone·webview
2501_915909062 小时前
HTML 开发工具有哪些?常用 HTML 开发工具推荐、学习路线与实战经验分享
android·小程序·https·uni-app·iphone·webview
wei8440678723 小时前
Android实现RecyclerView粘性头部效果,模拟微信账单列表的月份标题平移
android·java·微信·gitee
星空寻流年13 小时前
设计模式第五章(门面模式)
android·设计模式