MeasureSpec源码解读

article/2025/8/25 16:55:24

文章目录

  • MeasureSpec的源码
  • MeasureSpec与LayoutParams

今天来讲讲MeasureSpec吧。因为他与View的测量流程相关性很大,只有正确的理解了MeasureSpec的工作原理,我们才能更好的自定义View。那么MeasureSpec它的作用是什么呢?一般来说,它在很大程度上决定了一个View的尺寸规格。为什么是很大程度上呢?因为一个View的尺寸大小还可能和布局树中的父布局相关。想一想,如果你在你的子View中的布局参数写了match_parent,那么这个控件的大小是不是就和父布局相关了?所以说,我们今天的内容主要有以下两个方面,一个是MeasureSpec的源码,一个是MeasureSpec是怎么配合布局参数去测量View的大小的。

MeasureSpec的源码

MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式而SpecSize是指在某种测量模式下的规格大小。下面先看一下 MeasureSpec的源码,通过下面的代码,应该不难理解MeasureSpec的工作原理

    public static class MeasureSpec {private static final int MODE_SHIFT = 30;private static final int MODE_MASK  = 0x3 << MODE_SHIFT;/** @hide */@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})@Retention(RetentionPolicy.SOURCE)public @interface MeasureSpecMode {}/*** Measure specification mode: The parent has not imposed any constraint* on the child. It can be whatever size it wants.*/public static final int UNSPECIFIED = 0 << MODE_SHIFT;/*** Measure specification mode: The parent has determined an exact size* for the child. The child is going to be given those bounds regardless* of how big it wants to be.*/public static final int EXACTLY     = 1 << MODE_SHIFT;/*** Measure specification mode: The child can be as large as it wants up* to the specified size.*/public static final int AT_MOST     = 2 << MODE_SHIFT;public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,@MeasureSpecMode int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}@UnsupportedAppUsagepublic static int makeSafeMeasureSpec(int size, int mode) {if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {return 0;}return makeMeasureSpec(size, mode);}@MeasureSpecModepublic static int getMode(int measureSpec) {//noinspection ResourceTypereturn (measureSpec & MODE_MASK);}public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}static int adjust(int measureSpec, int delta) {final int mode = getMode(measureSpec);int size = getSize(measureSpec);if (mode == UNSPECIFIED) {// No need to adjust size for UNSPECIFIED mode.return makeMeasureSpec(size, UNSPECIFIED);}size += delta;if (size < 0) {Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +") spec: " + toString(measureSpec) + " delta: " + delta);size = 0;}return makeMeasureSpec(size, mode);}}

MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配,为了方便操作,其提供了打包和解包方法。SpecMode和SpecSize也是一个int值,一组 SpecMode和SpecSize可以打包为一个MeasureSpec,而一个MeasureSpec可以通过解包的形式来得出其原始的SpecMode和SpecSize,需要注意的是这里提到的MeasureSpec是指 MeasureSpec所代表的int值,而并非MeasureSpec本身

SpecMode主要有三类,每一类都有特殊的含义,如下表所示

测量模式意义
UNSPECIFIED父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
EXACTLY父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。
AT_MOST父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。

看完了MeasureSpec的源码,那么接下来就是MeasureSpec怎么与布局参数配合去测量了。

MeasureSpec与LayoutParams

在View 测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec来确定View测量后的宽/高。**MeasureSpec不是唯 一由LayoutParams决定的,LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View的宽/高。**这句话其实很好理解,毕竟子View是在父容器里面的,不可能子View要求什么就是什么。打个比方,假设你向你父亲要500w去买房,但是你父亲没有500w,只有100w,虽然他想给你500w,但是这在它给不了你那么多。所以说子View的测量还是需要结合父view的MeasureSpec。那么怎么决定的呢?下面就来解决这个问题。其实这个问题在android的viewGroup源码中有答案了,我们可以看一下ViewGroup的源码,getChildMeasureSpec这个方法,顾名思义这个方法是得到子View的MeasureSpec,而上面我们讲过,MeasureSpec里面包含了SpecMode和SpecSize,其中SpecSize就是子View的大小了。所以我们看一下是怎么得到子View的MeasureSpec

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {//这里得到的是父容器的specMode和specSizeint specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;//根据父容器不同的specMode,子View的测量方式也不一样switch (specMode) {//如果父容器的specMode是EXACTLY    case MeasureSpec.EXACTLY://childDimension>=0表示布局参数是具体的数值if (childDimension >= 0) {//这是时候子View的大小,就是子View要求的大小,如果超出父容器的空间怎么办?这里不同的ViewGroup会有不同的实现,如果是LinearLayout,那么最多只会显示父View的空间大小。resultSize = childDimension;//无论如何,子View是确定大小的,所以它的specMode就是EXACTLY的resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {//这是时候子View的布局参数是MATCH_PARENT,那么子View的空间大小就是剩余空间大小了。那么这个剩余空间大小是多少? Math.max(0, specSize - padding),最终算出来也是一个准确的值,所以它的specMode还是是EXACTLY的resultSize = size;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {//这是时候子View的布局参数是WRAP_CONTENT,那么子View的空间大小自己测量的大小了,注意这里需要二次测量才能最终知道子View的大小。但是无论子View的大小是多少,他都不能超过父容器的剩余空间,所以,这时候它的specMode就是AT_MOST了。以上就是父控件时EXACTLY的时候,获取子View的MeasureSpec的算法了,这种算法更重要的是理解resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on uscase MeasureSpec.AT_MOST:if (childDimension >= 0) {// Child wants a specific size... so be itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size. It can't be// bigger than us.resultSize = size;resultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to becase MeasureSpec.UNSPECIFIED:if (childDimension >= 0) {// Child wants a specific size... let him have itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size... find out how big it should// beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size.... find out how// big it should beresultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;resultMode = MeasureSpec.UNSPECIFIED;}break;}//noinspection ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

上面的算法,大家自己去写一下自定义ViewGroup就知道怎么回事了,后面两个具体就不讲了,因为如果理解了第一种情况,后面两种情况就是一样的道理的。

直接通过一个表格来总结一个父容器的MeasureSpec和子View的布局参数怎么决定子View的MeasureSpec的吧。
在这里插入图片描述
以上就是MeasureSpec的内容了,不难,但是需要大家去理解,只有理解了后续的自定义View才能更加的熟练。


http://chatgpt.dhexx.cn/article/pylkw5os.shtml

相关文章

理解 MeasureSpec

在开始本篇文章之前&#xff0c;我们先看一段代码&#xff1a; Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int expendSpec MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);super.onMeasure(widthMe…

对MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE 2, MeasureSpec.AT_MOST)的一点理解

之前 遇到ScrollView中嵌入ListView&#xff0c;GridView冲突的解决&#xff08;让ListView全显示出来&#xff09; 链接 网上查找资料&#xff0c;代码大致如下&#xff1a; import android.content.Context; import android.util.AttributeSet; import android.widget.ListV…

View的基本概念与MeasureSpec

1.基本概念 View的绘制是由measuer、layout、draw三个过程才能完整的绘制一个View&#xff0c;其中measure是测量View的宽、高&#xff0c;layout是为了确认View在父容器所在的位置&#xff0c;draw是负责在屏幕上将View绘制出来。View的绘制流程是从ViewRoot的performTraversa…

Android之:了解MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE 2,MeasureSpec.AT_MOST)

在自定义View和ViewGroup的时候&#xff0c;我们经常会遇到int型的 MeasureSpec 来表示一个组件的大小&#xff0c;这个变量里面不仅有组件的尺寸大小&#xff0c;还有大小的模式。 这个大小的模式&#xff0c;有点难以理解。在系统中组件的大小模式有三种&#xff1a; 1.精确…

Android开发 MeasureSpec介绍

搬家后的博客链接: IT客栈 www.itkezhan.org 在自定义View和ViewGroup的时候&#xff0c;我们经常会遇到int型的MeasureSpec来表示一个组件的大小&#xff0c;这个变量里面不仅有组件的尺寸大小&#xff0c;还有大小的模式。 这个大小的模式&#xff0c;有点难以理解。在系统中…

Android-测量规格(MeasureSpec)

目录 一、简介二、组成三、具体使用 一、简介 二、组成 测量规格(MeasureSpec)是由测量模式(mode)和测量大小(size)组成&#xff0c;共32位(int类型)&#xff0c;其中&#xff1a; 测量模式(mode)&#xff1a;占测量规格(MeasureSpec)的高2位&#xff1b;测量大小(size)&…

MeasureSpec学习 - 转

在自定义View和ViewGroup的时候&#xff0c;我们经常会遇到int型的 MeasureSpec 来表示一个组件的大小&#xff0c;这个变量里面不仅有组件的尺寸大小&#xff0c;还有大小的模式。 这个大小的模式&#xff0c;有点难以理解。在系统中组件的大小模式有三种&#xff1a; 1.精确…

MeasureSpec介绍

在自定义View和ViewGroup的时候&#xff0c;我们经常会遇到int型的MeasureSpec来表示一个组件的大小&#xff0c;这个变量里面不仅有组件的尺寸大小&#xff0c;还有大小的模式。 这个大小的模式&#xff0c;有点难以理解。在系统中组件的大小模式有三种&#xff1a; 1.精确模式…

Android 中MeasureSpec的创建规则

概述 在Android中&#xff0c;View的onMeasure()方法用来对控件进行测量&#xff0c;确定控件的宽高。该方法的两个参数widthMeasureSpec和heightMeasureSpec由父View计算后传入子view的measure()方法&#xff0c;再由子view的measure()方法传入onMeasure()方法&#xff0c;本…

关于google浏览器打不开网页问题之容易被忽略的点

其实google浏览器打不开 网页&#xff0c;原因网上有好多种&#xff0c;包括什么关闭防火墙、取消高级设置LAN单选框等&#xff0c;我也都试了&#xff0c;搞到最后要崩溃了&#xff0c;后来无意中&#xff0c;我输入一个http://baidu.com然后enter管用了&#xff0c;能打开页面…

谷歌浏览器打不开网页

今天起来发现谷歌浏览器和IE都打不开网页了&#xff0c;估计是我电脑代理又被修改了 在谷歌浏览器的设置--> 高级 --> 打开代理设置中 取消勾选即可修复问题。

关于谷歌浏览器打不开的解决方法

关于谷歌浏览器打不开的解决方法 打开Google,搜索&#xff0c;出现下面的问题&#xff0c;怎么解决呢&#xff0c;下面两种方法提供参考。 打开Google,首页显示输入网址&#xff0c;我们可以输入任意一个网址&#xff0c;例如www.baidu.com,然后就可以搜索了。 打开选项-设置…

谷歌浏览器(chrome)无法正常打开网页的解决办法

在网上看到许多新手想使用谷歌浏览器但是下载安装之后却无法打开网页 分析原因如下&#xff1a; 一般都是因为谷歌浏览器默认的地址栏搜索引擎为goole&#xff0c;由于goole属于国外的网站&#xff0c;我们访问是需要fan qiang才能访问的&#xff0c;所以无法打开网页。所以我…

chrome双击突然打不开的解决办法

这个也是没有想到&#xff0c;浪费了我挺长时间。我电脑的chrome突然打不开了&#xff0c;打不开的意思是双击了之后没有反应&#xff0c;但是其实是有打开进程的&#xff0c;这个就很坑。 网上搜了很多&#xff0c;什么把进程给杀掉的&#xff0c;重启电脑的&#xff0c;重装c…

Outlook 突然打不开

打开电脑正准备上班然后outlook崩了&#xff0c;报错建议我重装软件。。问题是现在用的都是365全家桶&#xff0c;也没办法单独重装一个outlook。盲试了一把repair居然修好了..再后来就经常用到它T_T..&#xff08;不是什么好事&#xff09; 首先有几种临时解决方法。 如果时间…

谷歌浏览器任何页面都打不开连设置也不能打开

谷歌浏览器任何页面都打不开 设置不能打开 找到谷歌右击选择属性 点击目标在最后加上 -no-sandbox即可解决 一定记得在 -no-sandbox前加上空格

谷歌浏览器网页打不开怎么办

首次下载谷歌浏览器&#xff0c;打开时在搜索框输入特定的网址会出现下面的界面 相信很多小伙伴也为此苦恼&#xff0c;今天小编为大家分享一个解决方法。 1.在其他其他浏览器搜索【极简插件】如下&#xff1a; 2.在右上角的搜索框里搜索【IGG】 点击推荐下载 3.将下载的压…

Chrome应用商店打不开问题

Chrome浏览器作为谷歌的亲儿子&#xff0c;实力方面很强。可是Chrome自从退出中国市场后一直对国内用户不太友好&#xff0c;但也还是有一批Chrome的忠实粉一直在用Chrome。 Chrome应用商店中有很多好玩实用的插件&#xff0c;我们在国内打开Chrome应用商店时是这样子的&#…

google打不开?更改一下chrome设置,畅通无阻玩谷歌

最近不知道是怎么回事&#xff0c;google&#xff08;谷歌&#xff09;一直打不开&#xff0c;gmail也是时断时续&#xff0c;十分痛苦。google服务对于的重要性不言而喻&#xff0c;特别是做 一些国外的项目&#xff0c;google的服务中断会严重影响收益。其实&#xff0c;只要…

sql 抛出异常raiserror()

说明 用于抛出一个异常或错误。这个错误可以被程序捕捉到。 实例 declare error_mes varchar(1000) set error_mes1314520886的ERP_ICStockBillEntry中间表数据的收料仓库编码不存在于系统中 raiserror(error_mes,13,1,张三)输出