MeasureSpec笔记

MeasureSpec笔记这个知识点经常的忘记 别人总结的很好 转载来一起学习 文章出处 http blog csdn net biezhihua article details 正文 View 最终测量尺寸由 View 本身和其父容器共同决定的 如何在一定程度上顺应爹的 意愿 呢 这主要体现在对 MeasureSpec 类的使用 MeasureSpec MODE 含义 UNSPECIFIED 未指定 爹

这个知识点经常的忘记,别人总结的很好,转载来一起学习。

文章出处:http://blog.csdn.net/biezhihua/article/details/

正文

View最终测量尺寸由View本身和其父容器共同决定的,如何在一定程度上顺应爹的“意愿”呢?这主要体现在对MeasureSpec类的使用。

MeasureSpec:

MODE 含义
UNSPECIFIED 未指定,爹不会对儿子做任何的束缚,儿子想要多大都可以,一般用于系统内部测试。
EXACTLY 完全,儿子多大爹心里有数,爹早已算好了
AT_MOST 至多,爹已经为儿子设置好了一个最大限制,儿子你不能比这个值大,不能再多了。

父容器所谓的“意愿”其实就是上述三个常量值的表现,既然如此我们就应该对这三个Mode常量做一下判断才行,要不然怎么知道爹的意图呢?

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 声明一个临时变量来存储计算出的测量值 int resultWidth = 0; // 读取宽度测量规格中的mode int parentModeWidth = MeasureSpec.getMode(widthMeasureSpec); // 读取宽度测量规格中的size int parentSizeWidth = MeasureSpec.getSize(widthMeasureSpec); // 如果爹心里有数 if (MeasureSpec.EXACTLY == parentModeWidth) { // 那么儿子也不要让爹难做,就取爹给的大小吧。 resultWidth = parentSizeWidth; } // 如果爹心里没数 else { // 那么儿子可要看看自己需要多大了 resultWidth = mSrc.getWidth(); // 如果爹给儿子的是一个限制值 if (MeasureSpec.AT_MOST == parentModeWidth) { // 那么儿子自己的需求就要跟爹的限制比比看谁小,那么就要谁 resultWidth = Math.min(resultWidth, parentSizeWidth) } }// end if int resultHeight = 0; int parentModeHeight = MeasureSpec.getMode(heightMeasureSpec); int parentSizeHeight = MeasureSpec.getSize(heightMeasureSpec); if (parentModeHeight == MeasureSpec.EXACTLY) { resultHeight = parentSizeHeight; } else { resultHeight = mSrc.getHeight(); if (parentModeHeight == MeasureSpec.AT_MOST) { resultHeight = Math.min(resultHeight, parentSizeHeight); } } // end if // 设置最终测量尺寸 setMeasuredDimension(resultWidth, resultHeight); }

如上代码所示,我们从父容器传来的MeasureSpec中分离出mode和size,size只是一个期望值我们需要根据mode来计算最终的size。如果父容器对子元素没有一个确切的大小,那么我们就需要尝试去计算子元素,也就是我们自定义View的大小,而这部分大小更多是由我们也就是开发者根据实际情况计算的,计算的同时也要考虑父容器的限制值,当mode为AT_MOST时size,是父容器给予我们的一个最大值,我们控件的大小不应该超过这个值。

正文2

在ViewGroup中,如何体现View测量的大小是和其父容器共同决定的呢?

涉及到,measureChildren、measureChild和measureChildWithMargins还有getChildMeasureSpec,见名知意这几个方法都跟ViewGroup测量子元素有关,其中measureChildWithMargins和measureChildren类似只是加入了对Margins外边距的处理,ViewGroup提供对子元素测量的方法从measureChildren开始:

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { final int size = mChildrenCount; final View[] children = mChildren; for (int i = 0; i < size; ++i) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } } 

measureChildren的逻辑很简单,通过父容器传入的widthMeasureSpec和heightMeasureSpec遍历子元素并调用measureChild方法去测量每一个子元素的宽高:

protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { // 获取子元素的布局参数 final LayoutParams lp = child.getLayoutParams(); /* * 将父容器的测量规格已经上下和左右的边距还有子元素本身的布局参数传入getChildMeasureSpec方法计算最终测量规格 */ final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); // 调用子元素的measure传入计算好的测量规格 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } 

这里我们主要就是看看getChildMeasureSpec方法是如何确定最终测量规格的:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) { // 获取父容器的测量模式和尺寸大小 int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); // 这个尺寸应该减去内边距的值 int size = Math.max(0, specSize - padding); // 声明临时变量存值 int resultSize = 0; int resultMode = 0; /* * 根据模式判断 */ switch (specMode) { case MeasureSpec.EXACTLY: // 父容器尺寸大小是一个确定的值 /* * 根据子元素的布局参数判断 */ if (childDimension >= 0) { //如果childDimension是一个具体的值 // 那么就将该值作为结果 resultSize = childDimension; // 而这个值也是被确定的 resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { //如果子元素的布局参数为MATCH_PARENT // 那么就将父容器的大小作为结果 resultSize = size; // 因为父容器的大小是被确定的所以子元素大小也是可以被确定的 resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { //如果子元素的布局参数为WRAP_CONTENT // 那么就将父容器的大小作为结果 resultSize = size; // 但是子元素的大小包裹了其内容后不能超过父容器 resultMode = MeasureSpec.AT_MOST; } break; case MeasureSpec.AT_MOST: // 父容器尺寸大小拥有一个限制值 /* * 根据子元素的布局参数判断 */ if (childDimension >= 0) { //如果childDimension是一个具体的值 // 那么就将该值作为结果 resultSize = childDimension; // 而这个值也是被确定的 resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { //如果子元素的布局参数为MATCH_PARENT // 那么就将父容器的大小作为结果 resultSize = size; // 因为父容器的大小是受到限制值的限制所以子元素的大小也应该受到父容器的限制 resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { //如果子元素的布局参数为WRAP_CONTENT // 那么就将父容器的大小作为结果 resultSize = size; // 但是子元素的大小包裹了其内容后不能超过父容器 resultMode = MeasureSpec.AT_MOST; } break; case MeasureSpec.UNSPECIFIED: // 父容器尺寸大小未受限制 /* * 根据子元素的布局参数判断 */ if (childDimension >= 0) { //如果childDimension是一个具体的值 // 那么就将该值作为结果 resultSize = childDimension; // 而这个值也是被确定的 resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { //如果子元素的布局参数为MATCH_PARENT // 因为父容器的大小不受限制而对子元素来说也可以是任意大小所以不指定也不限制子元素的大小 resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { //如果子元素的布局参数为WRAP_CONTENT // 因为父容器的大小不受限制而对子元素来说也可以是任意大小所以不指定也不限制子元素的大小 resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } // 返回封装后的测量规格 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); } 

我们可以看到一个View的大小由其父容器的测量规格MeasureSpec和View本身的布局参数LayoutParams共同决定,但是即便如此,最终封装的测量规格也是一个期望值,究竟有多大还是我们调用setMeasuredDimension方法设置的。上面的代码中有些朋友看了可能会有疑问为什么childDimension >= 0就表示一个确切值呢?原因很简单,因为在LayoutParams中MATCH_PARENT和WRAP_CONTENT均为负数、哈哈!!正是基于这点,Android巧妙地将实际值和相对的布局参数分离开来。

关于getchildMeasureSpec()的总结:

  1. 当View采用固定宽高时,不管父容器的MeasureSpec是什么,View的MeasureSpec都是精准模式并且遵循LayoutParams中的大小。
  2. 当View的宽高是match_parent时,如果父容器的模式是精准模式,那么View也是精准模式并且其大小是父容器的剩余空间;如果父容器的模式是最大模式,那么View也是最大模式并且其大小不会超过父容器的剩余空间。
  3. 当View的宽高是wrap_content时,不管父容器的模式是精准还是最大化,View的模式总是最大化并且大小不能超过父容器的剩余空间。






版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/232015.html原文链接:https://javaforall.net

(0)
上一篇 2025年11月7日 上午11:01
下一篇 2025年11月7日 上午11:22


相关推荐

  • TLE5012B磁编码器原理及优点,径向充磁磁铁的安装。AS5047、AS5048、AS5600、TLE5012、MA730

    TLE5012B磁编码器原理及优点,径向充磁磁铁的安装。AS5047、AS5048、AS5600、TLE5012、MA730巨磁阻 GMR 磁编码器以其独特的优点 在近两年的产品应用上十分流行 它体积小巧 安装方便 分辨率高 不受灰尘油渍影响 可非接触安装 便于与电机集成一体实现伺服控制 相较于光电编码器 GMR 磁编码器的寿命几乎无限长 没有红外二极管的光衰对寿命造成影响 另外光电编码器内部需要高精度光栅 对生产的安装精度 洁净度 器件质量有严格的要求 而磁编码器在这方面则宽松了很多 只要强磁与芯片的同心度不是太差 间隔不是太远 都能良好的输出信号 本文主要以 TLE5012B MA730 AS5047P D AS50

    2026年3月18日
    1
  • 云原生(三十六) | Kubernetes篇之Harbor入门和安装

    云原生(三十六) | Kubernetes篇之Harbor入门和安装Harbor 是一个用于存储和分发 Docker 镜像的企业级 Registry 服务器 作为一个企业级私有 Registry 服务器 Harbor 提供了更好的性能和安全 提升用户使用 Registry 构建和运行环境传输镜像的效率 Harbor 支持安装在多个 Registry 节点的镜像资源复制 镜像全部保存在私有 Registry 中 确保数据和知识产权在公司内部网络中管控 另外 Harbor 也提供了高级的安全特性 诸如用户管理 访问控制和活动审计等

    2026年3月18日
    2
  • volatile关键字的作用和原理「建议收藏」

    volatile关键字的作用和原理「建议收藏」文章目录一、volatile关键字的作用二、volatile的底层原理三、volatile的适用场景一、volatile关键字的作用二、volatile的底层原理三、volatile的适用场景

    2022年5月31日
    39
  • iphone一键转移_苹果手机如何一键转移数据 转移教程介绍

    iphone一键转移_苹果手机如何一键转移数据 转移教程介绍众所周知,要更换手机的话,旧手机上的数据怎么办还真的是一个难题啊,毕竟手机用久了,上面的有各种重要的数据不是。那苹果手机换机的话,将数据进行转移,可以分成两种情况,一种吧,就是苹果转苹果,一种就是苹果转安卓了。苹果手机数据转移到新iPhone1、借助iCloud云备份手机自带的云备份功能,肯定是可以用上的。①手机连接上WiFi,然后在手机“设置”中,依次点击“AppleID——iCloud——i…

    2022年5月25日
    59
  • sfm点云代码_SfM实现过程分析

    sfm点云代码_SfM实现过程分析昨天立了flag,今天要学SfM过程,大概看了看SfM的各个文件目录,build&make出来的linux-release-x86大概叫这个名字的文件夹里面有很多可执行文件,直接根据文档里给的参数跑就可以,要搞源码的话实在是搞不起,太复杂,太庞大了。下面的代码是从他给出的easytouse的python脚本中截取的核心代码,注释的也很赞,清晰明确。SfMglobalpipeline…

    2022年6月20日
    44
  • 前端跨域的几种方式

    前端跨域的几种方式前端跨域的几种方式

    2026年3月18日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号