利用 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())
}
相关推荐
lynn8570_blog40 分钟前
关于compose的remember
android·kotlin
毕设源码-邱学长1 小时前
【开题答辩全过程】以 基于安卓的外卖点餐APP的设计与实现为例,包含答辩的问题和答案
android
csj501 小时前
安卓基础之《(16)—内容提供者(2)使用内容组件获取通讯信息》
android
·云扬·1 小时前
ClickHouse常用管理语句汇总:会话、磁盘、性能与复制管理
android·clickhouse
游戏开发爱好者82 小时前
2025年iOS应用上架App Store全指南,开发者必看
android·ios·小程序·https·uni-app·iphone·webview
a3158238062 小时前
Android CardView修改背景阴影
android·cardview·修改背景
kk哥88992 小时前
Android UI 优化指南:流畅度与体验双提升
android·ui
摘星编程3 小时前
Flutter for OpenHarmony 实战:SliverList 滑动列表详解
android·javascript·flutter
abbiz3 小时前
30 个 Android 面试高频问题及答案
android·面试·职场和发展
冬奇Lab3 小时前
【Kotlin系列04】类与对象基础:从Java Bean到Data Class的优雅蜕变
android·kotlin·编程语言