目录
[1. 测量阶段 (onMeasure())](#1. 测量阶段 (onMeasure()))
[2. 布局阶段 (onLayout())](#2. 布局阶段 (onLayout()))
[3. 绘制阶段 (onDraw())](#3. 绘制阶段 (onDraw()))
在 Android 中,自定义 View 的绘制流程主要包括测量 、布局 、绘制 三个关键步骤。具体来说,自定义 View 的绘制涉及重写系统的 onMeasure()
、onLayout()
和 onDraw()
方法。下面详细介绍这个流程:
1. 测量阶段 (onMeasure()
)
onMeasure()
方法用于确定 View 的尺寸。系统会调用该方法来让自定义 View 计算其宽度和高度。你可以根据父布局给定的测量模式和尺寸对 View 进行自适应处理。
- 系统提供了三种测量模式:
EXACTLY
: 父布局强制给定的大小。自定义 View 必须使用该大小。AT_MOST
: 父布局允许的最大尺寸。View 可以小于或等于这个尺寸。UNSPECIFIED
: 父布局对 View 的尺寸没有任何限制。
示例:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 计算出自定义 View 的大小
int width = calculateWidth(widthMode, widthSize);
int height = calculateHeight(heightMode, heightSize);
// 调用 setMeasuredDimension 设置测量的宽高
setMeasuredDimension(width, height);
}
2. 布局阶段 (onLayout()
)
onLayout()
方法用于安排子 View 的位置。自定义 View 本身没有子 View 时,通常不需要重写此方法。只有自定义 ViewGroup(包含子 View 的容器)时才需要重写。
在 onLayout()
中,确定每个子 View 的摆放位置,可以通过 child.layout(left, top, right, bottom)
来安排子 View 的布局。
示例:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
// 如果有子 View,调用每个子 View 的 layout() 方法设置它们的位置
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
child.layout(left, top, right, bottom); // 设置子 View 的位置
}
}
3. 绘制阶段 (onDraw()
)
onDraw()
方法用于执行实际的绘制操作。开发者可以通过重写该方法来定义自定义 View 的外观。在 onDraw()
中,使用 Canvas
对象绘制图形、文本、图片等。
在 onDraw()
方法中:
- 使用
Canvas
提供的方法来绘制图形(如drawRect()
、drawCircle()
等)。 Paint
对象用于设置绘制样式,如颜色、线条粗细、字体等。
示例:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 设置画笔
Paint paint = new Paint();
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
// 画一个圆
canvas.drawCircle(getWidth() / 2, getHeight() / 2, Math.min(getWidth(), getHeight()) / 2, paint);
}
总体绘制流程
-
Measure : 首先调用
onMeasure()
方法,测量 View 的尺寸并传递给 View 的父布局。父布局基于测量结果决定如何分配空间给子 View。 -
Layout : 在测量结束后,调用
onLayout()
方法对 View 进行布局。对于自定义 ViewGroup,父布局会传递给子 View 的具体位置坐标。 -
Draw : 布局完成后,系统会调用
onDraw()
方法。onDraw()
主要负责绘制内容,例如图形、文字等。绘制顺序为:
- 背景
- 自身内容(如文字、图形)
- 子 View
- 滑动条等装饰
注意事项
- 在执行
onDraw()
时,确保绘制效率。如果绘制操作过于复杂,可能会导致卡顿,应尽量避免在onDraw()
中进行复杂的计算。 - 重写
onMeasure()
时,必须调用setMeasuredDimension()
来设置 View 的宽高,否则系统将无法正确布局该 View。 - 需要使用
invalidate()
来触发重新绘制操作。
示例总结
public class CustomView extends View {
public CustomView(Context context) {
super(context);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint);
}
}