咱们今天就用一个超有趣的故事来理解Android的View绘制原理。准备好了吗?
故事开始:UI王国的"画展"筹备记
想象一下,Android系统就像一个庞大的UI王国,每次打开App就像举办一场画展。
角色介绍:
- ViewRootImpl:画展总策划(大管家)
- DecorView:画展的巨型画布
- View/ViewGroup:各种画家和画师小组
- Choreographer:动画导演(负责节奏)
- SurfaceFlinger:最终展厅布置师
- CPU/GPU:画家们的双手
第一幕:画展的启动(Activity创建)
java
// 王国收到举办画展的指令
public class Activity {
public void onCreate() {
setContentView(R.layout.main); // "准备画布和颜料!"
}
}
当Activity创建时,PhoneWindow会准备DecorView这个"巨型画布",然后通过LayoutInflater把XML布局变成一个个View对象。
第二幕:三大核心流程 - 画展筹备三部曲
1. 测量(Measure)- "量尺寸"
总策划ViewRootImpl大喊:"各位画家,报上你们的尺寸!"
java
// ViewRootImpl.java
private void performTraversals() {
// 第一步:测量
performMeasure();
// 第二步:布局
performLayout();
// 第三步:绘制
performDraw();
}
// 测量过程 - 递归测量所有View
private void performMeasure() {
int childWidthMeasureSpec = ...;
int childHeightMeasureSpec = ...;
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
每个View的measure方法:
java
// View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
onMeasure(widthMeasureSpec, heightMeasureSpec); // 自定义测量逻辑
// 测量完成后,mMeasuredWidth/mMeasuredHeight就有值了
}
有趣比喻:就像画家们互相商量:"我要占多大地方?你有多宽?我有多高?"
2. 布局(Layout)- "定位置"
"现在开始布置展位!每个人站在哪里?"
java
// ViewGroup.java
@Override
public final void layout(int l, int t, int r, int b) {
// 先确定自己的位置
super.layout(l, t, r, b);
// 再安排子View的位置
layoutChildren();
}
protected void layoutChildren() {
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
// 计算每个子View的位置
child.layout(childLeft, childTop, childRight, childBottom);
}
}
3. 绘制(Draw)- "动笔作画"
"各就各位,开始画画!"
java
// View.java
public void draw(Canvas canvas) {
// 步骤1:画背景
drawBackground(canvas);
// 步骤2:画自己(核心)
onDraw(canvas);
// 步骤3:画子View
dispatchDraw(canvas);
// 步骤4:画装饰(滚动条、前景等)
onDrawForeground(canvas);
}
第三幕:同步与渲染 - 动画导演登场
Choreographer(动画导演)负责保持60fps的流畅节奏:
java
// Choreographer.java
public void onVsync() {
// 垂直同步信号到来!
mHandler.post(mTraversalRunnable);
}
final class TraversalRunnable implements Runnable {
public void run() {
doTraversal(); // 执行遍历(测量-布局-绘制)
}
}
第四幕:硬件加速 - GPU大神出手
现代Android使用硬件加速,就像请来了GPU这位"神笔马良":
java
// ThreadedRenderer.java(硬件渲染器)
@Override
void draw(View view, AttachInfo attachInfo) {
// 1. 创建DisplayList(绘制指令列表)
updateRootDisplayList(view);
// 2. 同步到RenderThread
nSyncAndDrawFrame(mNativeProxy);
}
DisplayList就像给GPU的"绘画说明书",避免重复计算。
第五幕:最终展示 - SurfaceFlinger布展
cpp
// SurfaceFlinger.cpp(Native层)
void SurfaceFlinger::composite() {
// 收集所有Layer(图层)
for (auto layer : mLayers) {
layer->latchBuffer(); // 获取图形缓冲区
layer->prepareClientComposition(); // 准备合成
}
// 最终合成并显示
doComposition();
postFramebuffer(); // 输出到屏幕
}
完整时序图:画展全流程

关键技巧:性能优化秘诀
- 减少层级:画展布置不要太复杂(View层级扁平化)
- 避免过度绘制:不要在同一区域反复画画
- 使用硬件加速:让GPU大神多干活
- View复用:像 recyclerView 一样,重复利用画布
java
// 优化示例:自定义View避免无效重绘
@Override
protected void onDraw(Canvas canvas) {
if (needRedraw) { // 只有需要时才重绘
drawContent(canvas);
needRedraw = false;
}
}
总结:一场精密的协作
Android View的绘制就像一场精心组织的画展:
- ViewRootImpl是总策划,协调整个流程
- 测量-布局-绘制是核心三部曲
- Choreographer保证60fps的流畅节奏
- GPU硬件加速提供强大的绘制能力
- SurfaceFlinger是最终的舞台总监
理解了这套机制,你就能写出更流畅的UI,成为真正的Android UI大师!
怎么样,同学,现在是不是对View绘制有了更生动的理解?