Android自定义View中的onMeasure、onLayout和onDraw方法解析

2024-03-24 18:20

本文主要是介绍Android自定义View中的onMeasure、onLayout和onDraw方法解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、onLayout、onMeasure和onDraw方法
    • 1.1 onMeasure(int widthMeasureSpec, int heightMeasureSpec)
      • 关于MeasureSpec
    • 1.2 onLayout(boolean changed, int left, int top, int right, int bottom)
    • 1.3 onDraw(Canvas canvas)
  • 二、自定义View案例
    • 2.1 创建CircleView类
    • 2.2 重写onMeasure方法
    • 2.3 重写onDraw方法
  • 三、自定义ViewGroup案例
    • 3.1 创建CustomLayout类
    • 3.2 重写onMeasure方法
    • 3.3 重写onLayout方法
  • 四、总结

在Android开发中,我们经常需要自定义View来实现特定的界面效果。要实现一个自定义View,我们需要了解并掌握onLayout、onMeasure和onDraw这三个关键方法。本文将详细介绍这三个方法的用法和解释,并给出两个自定义View的案例。

一、onLayout、onMeasure和onDraw方法

1.1 onMeasure(int widthMeasureSpec, int heightMeasureSpec)

onMeasure方法用于测量View的大小。在自定义View中,我们需要重写这个方法,根据View的宽高测量模式(MeasureSpec)来计算并设置View的宽高。

关于MeasureSpec

在Android中,MeasureSpec是一个32位的int值,用于描述View的宽度和高度信息。它由两部分组成:模式(mode)和尺寸(size)。模式占据高2位,尺寸占据低30位。

MeasureSpec有三种模式:

  1. EXACTLY:精确模式,对应于LayoutParams中的match_parent和具体的数值,表示父View希望子View的大小应该是一个确切的值。

  2. AT_MOST:最大模式,对应于LayoutParams中的wrap_content,表示子View的大小最多是指定的值,它可以决定自己的大小。

  3. UNSPECIFIED:未指定模式,通常在系统内部使用,表示父View没有给子View任何限制,子View可以设置任何大小。

widthMeasureSpec和heightMeasureSpec分别对应于View的宽度和高度信息。在测量View的过程中,父View会根据自己的尺寸和子View的LayoutParams,计算出合适的widthMeasureSpec和heightMeasureSpec,然后通过onMeasure方法传递给子View。

在onMeasure方法中,我们可以使用MeasureSpec.getMode和MeasureSpec.getSize方法来获取MeasureSpec的模式和尺寸。然后根据这些信息,计算并设置View的宽度和高度。

总的来说,MeasureSpec是Android中测量View大小的一个重要机制,它帮助我们理解和处理View的测量过程。

1.2 onLayout(boolean changed, int left, int top, int right, int bottom)

onLayout方法用于确定View的位置。在自定义ViewGroup中,我们需要重写这个方法,根据子View的测量宽高来确定它们的位置。

1.3 onDraw(Canvas canvas)

onDraw方法用于绘制View的内容。在自定义View中,我们需要重写这个方法,利用Canvas进行绘制操作,如绘制形状、文本、图片等。

二、自定义View案例

下面我们将通过一个简单的自定义View案例来演示如何使用这三个方法。我们将创建一个名为CircleView的自定义View,它会绘制一个带有边框的圆形。

2.1 创建CircleView类

首先,创建一个名为CircleView的类,继承自View,并实现构造方法。

public class CircleView extends View {public CircleView(Context context) {super(context);}public CircleView(Context context, AttributeSet attrs) {super(context, attrs);}public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}
}

2.2 重写onMeasure方法

在CircleView类中,重写onMeasure方法,根据MeasureSpec来计算并设置View的宽高。这里我们假设圆形的半径为100dp。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);int desiredSize = (int) (100 * getResources().getDisplayMetrics().density);int width = measureDimension(desiredSize, widthMode, widthSize);int height = measureDimension(desiredSize, heightMode, heightSize);setMeasuredDimension(width, height);
}private int measureDimension(int desiredSize, int mode, int size) {int result;if (mode == MeasureSpec.EXACTLY) {result = size;} else if (mode == MeasureSpec.AT_MOST) {result = Math.min(desiredSize, size);} else {result = desiredSize;}return result;
}

2.3 重写onDraw方法

在CircleView类中,重写onDraw方法,使用Canvas绘制圆形和边框。

@Override
protected void onDraw(Canvas canvas) {super.onDraw(canvas);int width = getWidth();int height = getHeight();int radius = Math.min(width, height) / 2;Paint paint = new Paint();paint.setAntiAlias(true);// 绘制圆形paint.setColor(Color.BLUE);canvas.drawCircle(width / 2, height / 2, radius, paint);// 绘制边框paint.setColor(Color.BLACK);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(5);canvas.drawCircle(width / 2, height / 2, radius - 2.5f, paint);
}

至此,我们已经完成了一个简单的自定义View——CircleView。在布局文件中使用这个自定义View,就可以看到一个带有边框的蓝色圆形。

通过这个案例,我们可以看到,onMeasure、onLayout和onDraw这三个方法在自定义View中的重要作用。onMeasure方法用于测量View的大小,onDraw方法用于绘制View的内容,而onLayout方法在此例中并未涉及,因为我们的CircleView直接继承自View,没有子View的布局需求。但如果我们需要自定义一个ViewGroup,那么onLayout方法将会用于确定子View的位置。

三、自定义ViewGroup案例

为了演示onLayout方法的使用,我们将创建一个名为CustomLayout的自定义ViewGroup,它将简单地将子View按照从左到右、从上到下的顺序排列。

3.1 创建CustomLayout类

首先,创建一个名为CustomLayout的类,继承自ViewGroup,并实现构造方法。

public class CustomLayout extends ViewGroup {public CustomLayout(Context context) {super(context);}public CustomLayout(Context context, AttributeSet attrs) {super(context, attrs);}public CustomLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}
}

3.2 重写onMeasure方法

在CustomLayout类中,重写onMeasure方法,根据MeasureSpec来计算并设置ViewGroup的宽高。这里我们假设每个子View的宽高为100dp,水平间距和垂直间距均为20dp。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 获取宽度和高度的测量模式和尺寸int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);// 定义子View的宽高和水平、垂直间距int childWidth = (int) (100 * getResources().getDisplayMetrics().density);int childHeight = (int) (100 * getResources().getDisplayMetrics().density);int horizontalSpacing = (int) (20 * getResources().getDisplayMetrics().density);int verticalSpacing = (int) (20 * getResources().getDisplayMetrics().density);// 初始化ViewGroup的宽高和当前行的宽高int width = 0;int height = 0;int rowWidth = 0;int rowHeight = childHeight;// 遍历所有子Viewfor (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);// 测量子View的大小measureChild(child, MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));// 更新当前行的宽度rowWidth += childWidth + horizontalSpacing;// 检查当前行宽度是否超过ViewGroup的宽度if (rowWidth > widthSize) {// 更新ViewGroup的宽度width = Math.max(width, rowWidth - horizontalSpacing);// 累加高度height += rowHeight + verticalSpacing;// 重置当前行的宽度rowWidth = childWidth + horizontalSpacing;}}// 更新ViewGroup的宽度和高度width = Math.max(width, rowWidth - horizontalSpacing);height += rowHeight;// 设置ViewGroup的测量宽高setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : width, heightMode == MeasureSpec.EXACTLY ? heightSize : height);
}

在这段代码中,我们首先获取宽度和高度的测量模式和尺寸。然后,定义子View的宽高和水平、垂直间距,并初始化ViewGroup的宽高和当前行的宽高。接着遍历所有的子View,测量子View的大小,并更新当前行的宽度。检查当前行宽度是否超过ViewGroup的宽度,如果超过了,更新ViewGroup的宽度,累加高度,并重置当前行的宽度。最后,更新ViewGroup的宽度和高度,并设置ViewGroup的测量宽高。

3.3 重写onLayout方法

在CustomLayout类中,重写onLayout方法,根据子View的测量宽高来确定它们的位置。

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {int width = getWidth();int childWidth = (int) (100 * getResources().getDisplayMetrics().density);int childHeight = (int) (100 * getResources().getDisplayMetrics().density);int horizontalSpacing = (int) (20 * getResources().getDisplayMetrics().density);int verticalSpacing = (int) (20 * getResources().getDisplayMetrics().density);int x = 0;int y = 0;for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);if (x + childWidth > width) {x = 0;y += childHeight + verticalSpacing;}child.layout(x, y, x + childWidth, y + childHeight);x += childWidth + horizontalSpacing;}
}

至此,我们已经完成了一个简单的自定义ViewGroup——CustomLayout。在布局文件中使用这个自定义ViewGroup,然后添加多个子View,就可以看到它们按照从左到右、从上到下的顺序排列。

通过这个案例,我们可以看到,onLayout方法在自定义ViewGroup中的重要作用。它用于确定子View的位置,根据子View的测量宽高来进行布局。在实际开发中,我们可以根据需求自定义不同的布局方式,实现各种复杂的界面效果。

四、总结

通过本文的介绍,我们了解了onLayout、onMeasure和onDraw这三个方法在自定义View和自定义ViewGroup中的作用和用法。onMeasure方法用于测量View的大小,onDraw方法用于绘制View的内容,onLayout方法用于确定View的位置。掌握这三个方法对于实现自定义View和自定义ViewGroup至关重要,有助于我们在实际开发中更好地满足设计需求,提高界面的交互性和美观性。

这篇关于Android自定义View中的onMeasure、onLayout和onDraw方法解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/842395

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

浅谈主机加固,六种有效的主机加固方法

在数字化时代,数据的价值不言而喻,但随之而来的安全威胁也日益严峻。从勒索病毒到内部泄露,企业的数据安全面临着前所未有的挑战。为了应对这些挑战,一种全新的主机加固解决方案应运而生。 MCK主机加固解决方案,采用先进的安全容器中间件技术,构建起一套内核级的纵深立体防护体系。这一体系突破了传统安全防护的局限,即使在管理员权限被恶意利用的情况下,也能确保服务器的安全稳定运行。 普适主机加固措施:

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

webm怎么转换成mp4?这几种方法超多人在用!

webm怎么转换成mp4?WebM作为一种新兴的视频编码格式,近年来逐渐进入大众视野,其背后承载着诸多优势,但同时也伴随着不容忽视的局限性,首要挑战在于其兼容性边界,尽管WebM已广泛适应于众多网站与软件平台,但在特定应用环境或老旧设备上,其兼容难题依旧凸显,为用户体验带来不便,再者,WebM格式的非普适性也体现在编辑流程上,由于它并非行业内的通用标准,编辑过程中可能会遭遇格式不兼容的障碍,导致操

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

【北交大信息所AI-Max2】使用方法

BJTU信息所集群AI_MAX2使用方法 使用的前提是预约到相应的算力卡,拥有登录权限的账号密码,一般为导师组共用一个。 有浏览器、ssh工具就可以。 1.新建集群Terminal 浏览器登陆10.126.62.75 (如果是1集群把75改成66) 交互式开发 执行器选Terminal 密码随便设一个(需记住) 工作空间:私有数据、全部文件 加速器选GeForce_RTX_2080_Ti

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s