一句话说透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 全量走
  • 优化层级少嵌套,流畅体验不用愁!
相关推荐
工程师老罗41 分钟前
如何在Android工程中配置NDK版本
android
Libraeking4 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
市场部需要一个软件开发岗位4 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
JMchen1236 小时前
Android后台服务与网络保活:WorkManager的实战应用
android·java·网络·kotlin·php·android-studio
crmscs7 小时前
剪映永久解锁版/电脑版永久会员VIP/安卓SVIP手机永久版下载
android·智能手机·电脑
localbob7 小时前
杀戮尖塔 v6 MOD整合版(Slay the Spire)安卓+PC端免安装中文版分享 卡牌肉鸽神作!杀戮尖塔中文版,电脑和手机都能玩!杀戮尖塔.exe 杀戮尖塔.apk
android·杀戮尖塔apk·杀戮尖塔exe·游戏分享
机建狂魔7 小时前
手机秒变电影机:Blackmagic Camera + LUT滤镜包的专业级视频解决方案
android·拍照·摄影·lut滤镜·拍摄·摄像·录像
hudawei9967 小时前
flutter和Android动画的对比
android·flutter·动画
lxysbly9 小时前
md模拟器安卓版带金手指2026
android
儿歌八万首10 小时前
硬核春节:用 Compose 打造“赛博鞭炮”
android·kotlin·compose·春节