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

相关文章

深度解析Spring Security 中的 SecurityFilterChain核心功能

《深度解析SpringSecurity中的SecurityFilterChain核心功能》SecurityFilterChain通过组件化配置、类型安全路径匹配、多链协同三大特性,重构了Spri... 目录Spring Security 中的SecurityFilterChain深度解析一、Security

Python安装Pandas库的两种方法

《Python安装Pandas库的两种方法》本文介绍了三种安装PythonPandas库的方法,通过cmd命令行安装并解决版本冲突,手动下载whl文件安装,更换国内镜像源加速下载,最后建议用pipli... 目录方法一:cmd命令行执行pip install pandas方法二:找到pandas下载库,然后

全面解析Golang 中的 Gorilla CORS 中间件正确用法

《全面解析Golang中的GorillaCORS中间件正确用法》Golang中使用gorilla/mux路由器配合rs/cors中间件库可以优雅地解决这个问题,然而,很多人刚开始使用时会遇到配... 目录如何让 golang 中的 Gorilla CORS 中间件正确工作一、基础依赖二、错误用法(很多人一开

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Linux系统中查询JDK安装目录的几种常用方法

《Linux系统中查询JDK安装目录的几种常用方法》:本文主要介绍Linux系统中查询JDK安装目录的几种常用方法,方法分别是通过update-alternatives、Java命令、环境变量及目... 目录方法 1:通过update-alternatives查询(推荐)方法 2:检查所有已安装的 JDK方

SQL Server安装时候没有中文选项的解决方法

《SQLServer安装时候没有中文选项的解决方法》用户安装SQLServer时界面全英文,无中文选项,通过修改安装设置中的国家或地区为中文中国,重启安装程序后界面恢复中文,解决了问题,对SQLSe... 你是不是在安装SQL Server时候发现安装界面和别人不同,并且无论如何都没有中文选项?这个问题也

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

Java Thread中join方法使用举例详解

《JavaThread中join方法使用举例详解》JavaThread中join()方法主要是让调用改方法的thread完成run方法里面的东西后,在执行join()方法后面的代码,这篇文章主要介绍... 目录前言1.join()方法的定义和作用2.join()方法的三个重载版本3.join()方法的工作原