在 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);
    }
}

参考资料

相关推荐
学逆向的12 小时前
汇编——函数
开发语言·汇编·网络安全
咸鱼翻身小阿橙12 小时前
多线程解析
java·开发语言
AOwhisky12 小时前
Python 学习笔记(第四期)——字符串:格式化、操作与文本处理——核心知识点自测与详解
开发语言·笔记·python·学习·列表·元组·字典
stevenzqzq13 小时前
app性能日志工具
android
DB哥讲数据库13 小时前
【最新】MySQL9.7安装教程:图文详解(附MySQL安装包)
android·mysql·adb
@@@@@@@@5413 小时前
JavaScript 的缺点与 TypeScript 的弥补
开发语言·javascript·typescript
tachibana213 小时前
hot100 课程表(207)
java·数据结构·算法·leetcode
神仙别闹13 小时前
编写基于C++ Huffman 算法的无损压缩程序和解压程序
java·c++·算法
এ慕ོ冬℘゜13 小时前
深度解析 JavaScript:赋予 Web 灵魂的“全栈语言”
开发语言·前端·javascript
IT乐手13 小时前
Android 写本地日志工具类
android·java