5.1 KiB
5.1 KiB
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 绘制流程?
答案:
- measure():测量大小
- layout():确定位置
- draw():绘制内容
Q2: measure 过程?
答案:
- 从父 View 到子 View 递归测量
- 使用 MeasureSpec 传递测量规格
- 子 View 测量后,父 View 确定自己的大小
Q3: layout 过程?
答案:
- 从父 View 到子 View 递归布局
- 确定每个 View 的位置
- 调用 onLayout() 方法
Q4: draw 过程?
答案:
- 绘制背景
- 绘制内容(onDraw)
- 绘制子 View(dispatchDraw)
- 绘制装饰
最后更新:2024年