利用 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())
}
相关推荐
coderlin_7 小时前
BI布局拖拽 (1) 深入react-gird-layout源码
android·javascript·react.js
2501_915918418 小时前
Fiddler中文版全面评测:功能亮点、使用场景与中文网资源整合指南
android·ios·小程序·https·uni-app·iphone·webview
wen's9 小时前
React Native安卓刘海屏适配终极方案:仅需修改 AndroidManifest.xml!
android·xml·react native
编程乐学10 小时前
网络资源模板--基于Android Studio 实现的聊天App
android·android studio·大作业·移动端开发·安卓移动开发·聊天app
没有了遇见12 小时前
Android 通过 SO 库安全存储敏感数据,解决接口劫持问题
android
hsx66612 小时前
使用一个 RecyclerView 构建复杂多类型布局
android
守城小轩12 小时前
Chromium 136 编译指南 - Android 篇:开发工具安装(三)
android·数据库·redis
whysqwhw13 小时前
OkHttp平台抽象机制分析
android
hsx66614 小时前
Android 内存泄漏避坑
android