Files
mkdocs/docs/android面试/系统原理/View绘制流程.md
2026-01-15 11:53:37 +08:00

5.1 KiB
Raw Permalink Blame History

View绘制流程

目录


View绘制流程

绘制流程

1. measure():测量 View 大小
2. layout():确定 View 位置
3. draw():绘制 View 内容

流程调用

// ViewRootImpl.performTraversals()
// 1. performMeasure()
// 2. performLayout()
// 3. performDraw()

measure过程

measure 方法

// View.measure()
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    // 1. 调用 onMeasure()
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    // 2. 保存测量结果
}

// View.onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(
        getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
        getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)
    );
}

MeasureSpec

// MeasureSpec测量规格
// 包含模式和大小

// 模式:
// 1. EXACTLY精确值
// 2. AT_MOST最大不超过
// 3. UNSPECIFIED未指定

int mode = MeasureSpec.getMode(measureSpec);
int size = MeasureSpec.getSize(measureSpec);

ViewGroup measure

// ViewGroup.onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 1. 测量所有子 View
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        measureChild(child, widthMeasureSpec, heightMeasureSpec);
    }
    // 2. 根据子 View 确定自己的大小
    setMeasuredDimension(width, height);
}

layout过程

layout 方法

// View.layout()
public void layout(int l, int t, int r, int b) {
    // 1. 保存位置
    setFrame(l, t, r, b);
    // 2. 调用 onLayout()
    onLayout(changed, l, t, r, b);
}

// View.onLayout():空实现
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

ViewGroup layout

// ViewGroup.onLayout()
protected abstract void onLayout(boolean changed, int l, int t, int r, int b);

// 子类实现,确定子 View 位置
@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(left, top, right, bottom);
    }
}

draw过程

draw 方法

// View.draw()
public void draw(Canvas canvas) {
    // 1. 绘制背景
    drawBackground(canvas);
    // 2. 保存图层
    // 3. 绘制内容
    onDraw(canvas);
    // 4. 绘制子 View
    dispatchDraw(canvas);
    // 5. 绘制装饰(滚动条等)
    onDrawForeground(canvas);
}

onDraw

// View.onDraw():绘制内容
protected void onDraw(Canvas canvas) {
    // 自定义绘制
    canvas.drawRect(rect, paint);
    canvas.drawText(text, x, y, paint);
}

dispatchDraw

// ViewGroup.dispatchDraw():绘制子 View
protected void dispatchDraw(Canvas canvas) {
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        drawChild(canvas, child, drawingTime);
    }
}

自定义View

自定义 View 步骤

// 1. 继承 View 或 ViewGroup
public class CustomView extends View {
    public CustomView(Context context) {
        super(context);
        init();
    }
    
    private void init() {
        // 初始化
    }
    
    // 2. 重写 onMeasure()
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width = measureWidth(widthMeasureSpec);
        int height = measureHeight(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }
    
    // 3. 重写 onDraw()
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 自定义绘制
        canvas.drawCircle(centerX, centerY, radius, paint);
    }
}

View绘制优化

优化方法

// 1. 减少布局层级
// 使用 ConstraintLayout 替代嵌套布局

// 2. 使用 ViewStub 延迟加载
<ViewStub
    android:id="@+id/view_stub"
    android:layout="@layout/heavy_layout" />

// 3. 避免过度绘制
// 移除不必要的背景

// 4. 使用硬件加速
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);

// 5. 优化 onDraw()
// 避免在 onDraw() 中创建对象
// 使用缓存

面试常见问题

Q1: View 绘制流程?

答案:

  1. measure():测量大小
  2. layout():确定位置
  3. draw():绘制内容

Q2: measure 过程?

答案:

  • 从父 View 到子 View 递归测量
  • 使用 MeasureSpec 传递测量规格
  • 子 View 测量后,父 View 确定自己的大小

Q3: layout 过程?

答案:

  • 从父 View 到子 View 递归布局
  • 确定每个 View 的位置
  • 调用 onLayout() 方法

Q4: draw 过程?

答案:

  1. 绘制背景
  2. 绘制内容onDraw
  3. 绘制子 ViewdispatchDraw
  4. 绘制装饰

最后更新2024年