MeasureSpec的理解和详尽源码分析

article/2025/8/25 17:05:22

package cc.ww;import android.view.View;
import android.view.View.MeasureSpec;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;/*** @author http://blog.csdn.net/lfdfhl* * 文档描述:* 关于MeasureSpec的理解* * (1) MeasureSpec基础知识*     MeasureSpec通常翻译为"测量规格",它是一个32位的int数据.*     其中高2位代表SpecMode即某种测量模式,低32位为SpecSize代表在该模式下的规格大小.*     可以通过:  *     int specMode = MeasureSpec.getMode(measureSpec) 获取specModeint specSize = MeasureSpec.getSize(measureSpec) 获取SpecSize常用的SpecMode有三种:MeasureSpec.EXACTLY官方文档Measure specification mode: The parent has determined an exact sizefor the child. The child is going to be given those bounds regardless of how big it wants to be.父容器已经检测出子View所需要的精确大小.该子View最终的测量大小即为SpecSize.(1) 当子View的LayoutParams的宽(高)采用具体的值(如100px)时且父容器的MeasureSpec为 MeasureSpec.EXACTLY或者MeasureSpec.AT_MOST或者MeasureSpec.UNSPECIFIED时:系统返回给该子View的specMode就为 MeasureSpec.EXACTLY系统返回给该子View的specSize就为子View自己指定的大小(childSize)通俗地理解:子View的LayoutParams的宽(高)采用具体的值(如100px)时,那么说明该子View的大小是非常明确的,明确到已经用具体px值指定的地步了.那么此时不管父容器的specMode是什么,系统返回给该子View的specMode总是MeasureSpec.EXACTLY,并且系统返回给该子View的specSize就为子View自己指定的大小(childSize).(2) 当子View的LayoutParams的宽(高)采用match_parent时并且父容器的MeasureSpec为 MeasureSpec.EXACTLY时:系统返回给该子View的specMode就为 MeasureSpec.EXACTLY系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize)通俗地理解:子View的LayoutParams的宽(高)采用match_parent时并且父容器的MeasureSpec为 MeasureSpec.EXACTLY.这时候说明子View的大小还是挺明确的:就是要和父容器一样大,更加直白地说就是父容器要怎样子View就要怎样.所以,如果父容器MeasureSpec为 MeasureSpec.EXACTLY那么:系统返回给该子View的specMode就为 MeasureSpec.EXACTLY,和父容器一样.系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize),就是父容器的剩余大小.同样的道理如果此时,MeasureSpec为 MeasureSpec.AT_MOST呢?系统返回给该子View的specMode也为 MeasureSpec.AT_MOST,和父容器一样.系统返回给该子View的specSize也为该父容器剩余空间的大小(parentLeftSize),就是父容器的剩余大小.MeasureSpec.AT_MOST官方文档The child can be as large as it wants up to the specified size.父容器指定了一个可用大小即specSize,子View的大小不能超过该值.(1) 当子View的LayoutParams的宽(高)采用match_parent时并且父容器的MeasureSpec为 MeasureSpec.AT_MOST时:系统返回给该子View的specMode就为 MeasureSpec.AT_MOST系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize)这种情况已经在上面介绍 MeasureSpec.EXACTLY时已经讨论过了.(2) 当子View的LayoutParams的宽(高)采用wrap_content时并且父容器的MeasureSpec为 MeasureSpec.EXACTLY时:系统返回给该子View的specMode就为 MeasureSpec.AT_MOST系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize)通俗地理解:子View的LayoutParams的宽(高)采用wrap_content时说明这个子View的宽高不明确,要视content而定.这个时候如果父容器的MeasureSpec为 MeasureSpec.EXACTLY即父容器是一个精确模式;这个时候简单地说子View是不确定的,父容器是确定的,那么系统返回给该子View的specMode也就是不确定的即为 MeasureSpec.AT_MOST系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize)(3) 当子View的LayoutParams的宽(高)采用wrap_content时并且父容器的MeasureSpec为 MeasureSpec.AT_MOST时:系统返回给该子View的specMode就为 MeasureSpec.AT_MOST系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize)通俗地理解:子View的LayoutParams的宽(高)采用wrap_content时说明这个子View的宽高不明确,要视content而定.这个时候如果父容器的MeasureSpec为 MeasureSpec.AT_MOST这个时候简单地说子View是不确定的,父容器也是不确定的,那么系统返回给该子View的specMode也就是不确定的即为 MeasureSpec.AT_MOST系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize)MeasureSpec.UNSPECIFIED官方文档The parent has not imposed any constraint on the child. It can be whatever size it wants.父容器不对子View的大小做限制.一般用作Android系统内部,或者ListView和ScrollView.在此不做讨论.关于这个三种测量规格下面的源码分析中体现得很明显,也可参考以下附图.* (2) 在onMeasure()时子View的MeasureSpec的形成过程分析*     关于该技术点的讨论,请看下面的源码分析.**/
public class UnderstandMeasureSpec {/*** 第一步:* 在ViewGroup测量子View时会调用到measureChildWithMargins()方法,或者与之类似的方法.* 请注意方法的参数:* @param child* 子View* @param parentWidthMeasureSpec* 父容器(比如LinearLayout)的宽的MeasureSpec* @param widthUsed* 父容器(比如LinearLayout)在水平方向已经占用的空间大小* @param parentHeightMeasureSpec* 父容器(比如LinearLayout)的高的MeasureSpec* @param heightUsed* 父容器(比如LinearLayout)在垂直方向已经占用的空间大小* * 在该方法中主要有四步操作,其中很重要的是调用了getChildMeasureSpec()方法来确定* 子View的MeasureSpec.详情参见代码分析*/protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {//1 得到子View的LayoutParamsfinal MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//2 得到子View的宽的MeasureSpecfinal int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);//3 得到子View的高的MeasureSpecfinal int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);//4 测量子Viewchild.measure(childWidthMeasureSpec, childHeightMeasureSpec);}/*** getChildMeasureSpec()方法确定子View的MeasureSpec* 请注意方法的参数:* @param spec* 父容器(比如LinearLayout)的宽或高的MeasureSpec* @param padding* 父容器(比如LinearLayout)在垂直方向或者水平方向已被占用的空间.* 在measureChildWithMargins()方法里调用getChildMeasureSpec()时注意第二个参数的构成:* 比如:mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin* 其中:* mPaddingLeft和mPaddingRight表示父容器左右两内侧的padding* lp.leftMargin和lp.rightMargin表示子View左右两外侧的margin* 这四部分都不可以再利用起来布局子View.所以说这些值的和表示:* 父容器在水平方向已经被占用的空间* 同理:* mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin* 表示:* 父容器(比如LinearLayout)在垂直方向已被占用的空间.* @param childDimension* 通过子View的LayoutParams获取到的子View的宽或高* * * 经过以上分析可从getChildMeasureSpec()方法的第一个参数和第二个参数可以得出一个结论:* 父容器(如LinearLayout)的MeasureSpec和子View的LayoutParams共同决定了子View的MeasureSpec!!!* * * */public static int getChildMeasureSpec(int spec, int padding, int childDimension) {/*** 第一步:得到父容器的specMode和specSize*/int specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);/*** 第二步:得到父容器在水平方向或垂直方向可用的最大空间值.*        关于padding参见上面的分析*/int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;/*** 第三步:确定子View的specMode和specSize.*        在此分为三种情况进行.*/switch (specMode) {/*** 第一种情况:* 父容器的测量模式为EXACTLY* * 请注意两个系统常量:* LayoutParams.MATCH_PARENT=-1* LayoutParams.WRAP_CONTENT=-2* 所以在此处的代码:* childDimension >= 0 表示子View的宽或高不是MATCH_PARENT和WRAP_CONTENT*/case MeasureSpec.EXACTLY:/*** 当父容器的测量模式为EXACTLY时如果:* 子View的宽或高是一个精确的值,比如100px;* 那么:* 子View的size就是childDimension* 子View的mode也为MeasureSpec.EXACTLY*/if (childDimension >= 0) {resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;/*** 当父容器的测量模式为EXACTLY时如果:* 子View的宽或高是LayoutParams.MATCH_PARENT* 那么:* 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size* 子View的mode也为MeasureSpec.EXACTLY*/} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize = size;resultMode = MeasureSpec.EXACTLY;/*** 当父容器的测量模式为EXACTLY时如果:* 子View的宽或高是LayoutParams.WRAP_CONTENT* 那么:* 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size* 子View的mode为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;/*** 第二种情况:* 父容器的测量模式为AT_MOST* * 请注意两个系统常量:pp* LayoutParams.MATCH_PARENT=-1* LayoutParams.WRAP_CONTENT=-2* 所以在此处的代码:* childDimension >= 0 表示子View的宽或高不是MATCH_PARENT和WRAP_CONTENT*/case MeasureSpec.AT_MOST:/*** 当父容器的测量模式为AT_MOST时如果:* 子View的宽或高是一个精确的值,比如100px;* 那么:* 子View的size就是childDimension* 子View的mode也为MeasureSpec.EXACTLY*/if (childDimension >= 0) {// Child wants a specific size... so be itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;/*** 当父容器的测量模式为AT_MOST时如果:* 子View的宽或高为LayoutParams.MATCH_PARENT* 那么:* 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size* 子View的mode也为MeasureSpec.AT_MOST*/} 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;/*** 当父容器的测量模式为AT_MOST时如果:* 子View的宽或高为LayoutParams.WRAP_CONTENT* 那么:* 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size* 子View的mode也为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;/*** 第三种情况:* 父容器的测量模式为UNSPECIFIED* * 请注意两个系统常量:* LayoutParams.MATCH_PARENT=-1* LayoutParams.WRAP_CONTENT=-2* 所以在此处的代码:* childDimension >= 0 表示子View的宽或高不是MATCH_PARENT和WRAP_CONTENT*/case MeasureSpec.UNSPECIFIED:/*** 当父容器的测量模式为UNSPECIFIED时如果:* 子View的宽或高是一个精确的值,比如100px;* 那么:* 子View的size就是childDimension* 子View的mode也为MeasureSpec.EXACTLY*/if (childDimension >= 0) {// Child wants a specific size... let him have itresultSize = childDimension;resultMode = MeasureSpec.EXACTLY;/*** 当父容器的测量模式为UNSPECIFIED时如果:* 子View的宽或高为LayoutParams.MATCH_PARENT* 那么:* 子View的size为0* 子View的mode也为MeasureSpec.UNSPECIFIED*/} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size... find out how big it should beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;/*** 当父容器的测量模式为UNSPECIFIED时如果:* 子View的宽或高为LayoutParams.WRAP_CONTENT* 那么:* 子View的size为0* 子View的mode也为MeasureSpec.UNSPECIFIED*/} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size.... find out how big it should beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;}break;}return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}}








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

相关文章

自定义View 测量过程(Measure)

目录 一、作用二、储备知识2.2 ViewGroup.LayoutParams2.3 MeasureSpec 三、measure过程详解3.1 单一View的measure过程具体流程源码分析源码总结 3.2 ViewGroup的measure过程测量原理具体流程源码分析流程总结 四、总结 一、作用 测量View的宽 / 高 在某些情况下,…

Android MeasureSpec解析

1. MeasureSpec组成 MeasureSpec是View的一个内部类,由一个32位的int值组成,前两位代表SpecMode测量模式,后30位代表SpecSize大小值。 其中测量模式共有三种: EXACTLY(确定):父控件为子View指…

使用View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED)在在onCreate中获得控件的大小问题

android 在onCreate中获得控件的大小 int w View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); int h View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); edt_height.measure(w, h); int height edt_height.getMeasuredHeight(); int w…

MeasureSpec中三种模式:UNSPECIFIED,AT_MOST,EXACTLY

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

评测指标(metrics)

评测指标(metrics) metric主要用来评测机器学习模型的好坏程度,不同的任务应该选择不同的评价指标, 分类,回归和排序问题应该选择不同的评价函数. 不同的问题应该不同对待,即使都是 分类问题也不应该唯评价函数论,不同问题不同分析. 回归(Regression) 均方误差(MSE) (1) l ( y…

MeasureSpec学习—对Integer.MAX_VALUE 2的认识

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

理解Android中的MeasureSpec

文章收藏的好句子:永远要相信美好的事情即将发生。 ps:本文源码是基于 Android Api 31 来分析的 目录 1、MeasureSpec 1、1 SpecMode 1、2 MeasureSpec 的 int 值和 LayoutParams 的对应关系 1、MeasureSpec 我们在 Android 手机上看到的界面&#xff0c…

android Measurespec测量模式

MeasureSpecs 类 1、是一个32位的二进制数,由模式(mode)和大小(size)组成, 2、其中:32和31位代表测量模式(mode)、后30位代表测量大小(size) 3、…

MeasureSpec源码解读

文章目录 MeasureSpec的源码MeasureSpec与LayoutParams 今天来讲讲MeasureSpec吧。因为他与View的测量流程相关性很大,只有正确的理解了MeasureSpec的工作原理,我们才能更好的自定义View。那么MeasureSpec它的作用是什么呢?一般来说&#xff…

理解 MeasureSpec

在开始本篇文章之前,我们先看一段代码: 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,GridView冲突的解决(让ListView全显示出来) 链接 网上查找资料,代码大致如下: import android.content.Context; import android.util.AttributeSet; import android.widget.ListV…

View的基本概念与MeasureSpec

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

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

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

Android开发 MeasureSpec介绍

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

Android-测量规格(MeasureSpec)

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

MeasureSpec学习 - 转

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

MeasureSpec介绍

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

Android 中MeasureSpec的创建规则

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

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

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

谷歌浏览器打不开网页

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