利用 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())
}
相关推荐
android_xc19 分钟前
Android Studio国内仓库配置
android·ide·android studio
alexhilton21 分钟前
runBlocking实践:哪里该使用,哪里不该用
android·kotlin·android jetpack
2501_915106321 小时前
iOS 使用记录和能耗监控实战,如何查看电池电量消耗、App 使用时长与性能数据(uni-app 开发调试必备指南)
android·ios·小程序·uni-app·cocoa·iphone·webview
雨白1 小时前
深入解析 Android 多点触摸:从原理到实战
android
曾经的三心草2 小时前
Python2-工具安装使用-anaconda-jupyter-PyCharm-Matplotlib
android·java·服务器
Jerry3 小时前
Compose 设置文字样式
android
飞猿_SIR3 小时前
android定制系统完全解除应用安装限制
android
索迪迈科技4 小时前
影视APP源码 SK影视 安卓+苹果双端APP 反编译详细视频教程+源码
android·影视app源码·sk影视
孔丘闻言4 小时前
python调用mysql
android·python·mysql
萧雾宇6 小时前
Android Compose打造仿现实逼真的烟花特效
android·flutter·kotlin