# View绘制流程

## 目录
- [View绘制流程](#view绘制流程)
- [measure过程](#measure过程)
- [layout过程](#layout过程)
- [draw过程](#draw过程)
- [自定义View](#自定义view)
- [View绘制优化](#view绘制优化)
- [面试常见问题](#面试常见问题)

---

## View绘制流程

### 绘制流程

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

### 流程调用

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

---

## measure过程

### measure 方法

```java
// 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

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

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

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

### ViewGroup measure

```java
// 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 方法

```java
// 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

```java
// 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 方法

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

### onDraw

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

### dispatchDraw

```java
// 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 步骤

```java
// 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绘制优化

### 优化方法

```java
// 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. 绘制子 View（dispatchDraw）
4. 绘制装饰

---

*最后更新：2024年*
