利用 onMeasure、onLayout、onDraw 创建自定义 View

在 Android 中,自定义 View 是实现特殊 UI 效果和控件的重要方式。掌握 onMeasureonLayoutonDraw 这三个方法,是写好自定义 View 的关键。本文通过一个示例,加强对这三个方法的理解。

三个关键方法介绍

1. onMeasure(int widthMeasureSpec, int heightMeasureSpec)

作用:测量 View 的尺寸。告诉系统"我希望的大小是多少"。

  • widthMeasureSpecheightMeasureSpec 是测量规格,包含模式和尺寸。
  • 根据父控件的要求和自己的内容,计算并调用 setMeasuredDimension(width, height),设置 View 最终尺寸。

简单理解: 测量阶段,告诉系统你需要多大空间。

2. onLayout(boolean changed, int left, int top, int right, int bottom)

作用:确定 View 在父布局中的位置和大小。

  • 对于普通 View,一般不用重写,因为它的位置由父布局决定。
  • 对于 ViewGroup(容器),重写此方法来布局子控件的位置。

简单理解: 布局阶段,确定View摆放的具体位置。

3. onDraw(Canvas canvas)

作用:绘制 View 的内容。

  • 这里写绘图代码,使用 Canvas 画图形、文本、图片等。
  • 只有 View 被测量和布局完后,系统才会调用 onDraw

简单理解: 绘制阶段,把你的内容画出来。

通过示例理解三者配合

需求

实现两个子View互相重叠的效果,并且绘制一个背景色

效果图

代码内容

初始化两个不同颜色的子view

js 复制代码
class CustomLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : ViewGroup(context, attrs, defStyleAttr) {

    init {
        val view1 = View(context)
        view1.setBackgroundColor("#FF0000".toColorInt())
        addView(view1)

        val view2 = View(context)
        view2.setBackgroundColor("#00FF00".toColorInt())
        addView(view2)

        setWillNotDraw(false)
    }
    ...
}

先测量两个子View,扣去重叠大小后,计算出当前View的最终大小

js 复制代码
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    // 重叠大小
    val overlapping = 60
    // 子控件宽高
    val itemWidth = 400
    val itemHeight = 300

    // 先测量子控件
    for (i in 0 until 2) {
        val view = getChildAt(i)
        measureChild(
            view,
            MeasureSpec.makeMeasureSpec(itemWidth, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(itemHeight, MeasureSpec.EXACTLY)
        )
    }

    // 本身大小
    val width = itemWidth * 2 - overlapping
    val height = itemHeight * 2 - overlapping
    setMeasuredDimension(width, height)
}

放置两个子View

js 复制代码
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
    // 放置在右上角
    val view1 = getChildAt(0)
    view1.layout(measuredWidth - view1.measuredWidth, 0, measuredWidth, view1.measuredHeight)

    // 放置在左下角
    val view2 = getChildAt(1)
    view2.layout(0, measuredHeight - view2.measuredHeight, view2.measuredWidth, measuredHeight)
}

绘制背景色

js 复制代码
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    // 绘制一个浅色的背景
    canvas.drawColor("#FFF0F0F0".toColorInt())
}
相关推荐
_李小白1 小时前
【Android 美颜相机】第二十三天:GPUImageDarkenBlendFilter(变暗混合滤镜)
android·数码相机
小天源4 小时前
银河麒麟 V10(x86_64)离线安装 MySQL 8.0
android·mysql·adb·麒麟v10
2501_915921434 小时前
傻瓜式 HTTPS 抓包,简单抓取iOS设备数据
android·网络协议·ios·小程序·https·uni-app·iphone
csj505 小时前
安卓基础之《(20)—高级控件(2)列表类视图》
android
JMchen1235 小时前
Android计算摄影实战:多帧合成、HDR+与夜景算法深度剖析
android·经验分享·数码相机·算法·移动开发·android-studio
恋猫de小郭7 小时前
Flutter 在 Android 出现随机字体裁剪?其实是图层合并时的边界计算问题
android·flutter·ios
2501_915918417 小时前
把 iOS 性能监控融入日常开发与测试流程的做法
android·ios·小程序·https·uni-app·iphone·webview
benjiangliu8 小时前
LINUX系统-09-程序地址空间
android·java·linux
独自破碎E8 小时前
字符串相乘
android·java·jvm
DokiDoki之父9 小时前
边写软件边学kotlin(一):Kotlin语法初认识:
android·开发语言·kotlin