在 Android 中,自定义 View 的绘制流程

目录

[1. 测量阶段 (onMeasure())](#1. 测量阶段 (onMeasure()))

[2. 布局阶段 (onLayout())](#2. 布局阶段 (onLayout()))

[3. 绘制阶段 (onDraw())](#3. 绘制阶段 (onDraw()))

总体绘制流程

注意事项

示例总结

参考资料


在 Android 中,自定义 View 的绘制流程主要包括测量布局绘制 三个关键步骤。具体来说,自定义 View 的绘制涉及重写系统的 onMeasure()onLayout()onDraw() 方法。下面详细介绍这个流程:

1. 测量阶段 (onMeasure())

onMeasure() 方法用于确定 View 的尺寸。系统会调用该方法来让自定义 View 计算其宽度和高度。你可以根据父布局给定的测量模式和尺寸对 View 进行自适应处理。

  • 系统提供了三种测量模式:
    1. EXACTLY: 父布局强制给定的大小。自定义 View 必须使用该大小。
    2. AT_MOST: 父布局允许的最大尺寸。View 可以小于或等于这个尺寸。
    3. 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);
}

总体绘制流程

  1. Measure : 首先调用 onMeasure() 方法,测量 View 的尺寸并传递给 View 的父布局。父布局基于测量结果决定如何分配空间给子 View。

  2. Layout : 在测量结束后,调用 onLayout() 方法对 View 进行布局。对于自定义 ViewGroup,父布局会传递给子 View 的具体位置坐标。

  3. 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);
    }
}

参考资料

相关推荐
CUMT_DJ33 分钟前
matlab计算算法的运行时间
开发语言·算法·matlab
SUPER52662 小时前
FastApi项目启动失败 got an unexpected keyword argument ‘loop_factory‘
java·服务器·前端
咕噜咕噜啦啦2 小时前
Eclipse集成开发环境的使用
java·ide·eclipse
Overboom4 小时前
[C++] --- 常用设计模式
开发语言·c++·设计模式
Univin4 小时前
C++(10.4)
开发语言·数据结构·c++
KyollBM4 小时前
每日羊题 (质数筛 + 数学 | 构造 + 位运算)
开发语言·c++·算法
alexhilton4 小时前
突破速度障碍:非阻塞启动画面如何将Android 应用启动时间缩短90%
android·kotlin·android jetpack
光军oi5 小时前
全栈开发杂谈————关于websocket若干问题的大讨论
java·websocket·apache
kobe_OKOK_5 小时前
Django `models.Field` 所有常见配置参数的完整清单与说明表
android
weixin_419658315 小时前
Spring 的统一功能
java·后端·spring