MeasureSpec——View测量过程中的建桥


一.什么是MeasureSpec

在View工作的流程中,measure过程决定view的宽高,在view的measure过程中,MeasureSpec起到了至关重要的作用,它参与了Measure的测量过程。
我们知道,一个view的宽高有时受到父容器的影响,在测量工程中,系统会将view的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,再根据这个MeasureSpec测量出View的宽高,所以View很大程度上决定了view的尺寸规格。所以这个很抽象的MeasureSpec我们可以把它理解为“策略规格书”。

二.探索MeasureSpec

先看下View的内部类MeasureSpec的内容:

private static final int MODE_SHIFT = 30;
private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY     = 1 << MODE_SHIFT;
public static final int AT_MOST     = 2 << MODE_SHIFT;public static int makeMeasureSpec(int size, int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}public static int getMode(int measureSpec) {return (measureSpec & MODE_MASK);}public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}

MeasureSpec代表一个32位的int值,通过SpecMode和SpecSize组合成一个int来节约内存,其中高2位代表SpecMode,指测量模式,低30位代表SpecSize,指该测量模式下的规格大小。通过getMode和getSize可以得到其中的值。

通过让0,1,2向左移位30定义了三个常量作为SpecMode的三种类型:

EXACTLY

检查出view的精确大小,此时view的宽高就等于SpecSize的宽高。

AT_MOST

父容器指定了一个可用大小的SpecSize,view宽高不能大于这个值。
用于wrapcontent时,内容包裹大小,但view大小不会超过父容器大小。

UNSPECIFIED

对view不做任何限制,不常出现,多用于scrollview这种要显示内容远大于可显示区域的情况。

二.MeasureSpec与LayoutParams

举个例子,这是自定义view的时候重写onMeasure中的一小段:

int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height;
if (heightMode == MeasureSpec.EXACTLY) {height = heightSize;} else {height = getPaddingTop() + mBound.height() + getPaddingBottom();if(heightMode==MeasureSpec.AT_MOST){height = Math.min(height,heightSize);}}

可以看出,这里通过条件判断SpecMode对view的高进行了约束,一般情况下,我们可以给view设置LayoutParams,比如宽高或者wrapcontent,细心的同学会发现重写onMeasure的时候我们会发现传了两个参数,宽高的MeasureSpec,这两个参数是伴随父容器传下来的,它会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后根据这个MS来确定View的宽高。

对于一般的View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。

那对于顶级View(DecorView)来说没有上层父容器了,所以其MeasureSpec由窗口的尺寸和自身的LayoutParams来决定。

三.MeasureSpec的创建过程

顶级view的MeasureSpec的创建过程与普通view有所不同,MeasureSpec由窗口的尺寸和自身的LayoutParams来决定,很好理解,父容器=屏幕。这里代码就不贴了。
总来就说就是跟屏幕尺寸有关:

  • 如果设置matchparent:大小就是屏幕大小。
  • wrapcontent:内容包裹,大小不能超过屏幕大小。
  • 固定大小(android:layout_height=”100dp”):指定多大就是多大。

那么对于普通View来说,measure过程由viewGroup传递过来,
viewGroup中的measureChildWithMargin方法:

    protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin+ heightUsed, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);//调用子元素的measure方法}

注意child.measure传的参数就是我们之前所说的重写onMeasure方法得到的那两个参数,通过getChildMeasureSpec方法(注意传的参数)来得到子元素的MeasureSpec,所以解释了为什么子元素MeasureSpec的创建与父容器的的MeasureSpec和子元素和子元素本身的LayoutParams有关,还与View的margin和padding有关:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);int specSize = MeasureSpec.getSize(spec);//size为子容器可用大小,为父容器减去padding的大小int size = Math.max(0, specSize - padding);int resultSize = 0;int resultMode = 0;switch (specMode) {// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:if (childDimension >= 0) {resultSize = childDimension;resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size. So be it.resultSize = size;resultMode = MeasureSpec.EXACTLY;} 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 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 = 0;resultMode = 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);}
......//省略部分}

上述measureChildWithMargin方法传递了参数为父容器的MeasureSpec和子容器的padding=mPaddingTop+mPaddingBottom+lp.topMargin+lp.bottomMargin+ heightUsed(还有一组宽度),通过父容器的MeasureSpec结合view本身的LayoutParams来确定子元素的MeasureSpec。
那么总结来说:

  • view采用固定模式:

    • 不管父容器的SpecMode是什么,view的MeasureSpec都是EXACTLY,大小就是开发者设置的大小。
  • View是matchparent:
    • 父容器为EXACTLY,那么view也是EXACTLY,大小是父容器的剩余空间。
    • 父容器是AT_MOST,view也是AT_MOST,大小不能超过父容器的剩余大小。
  • view是wrapcontent:
    • 不管父容器为什么,view的模式总是AT_MOST,大小不超过父容器的剩余大小。
  • view为UNSPECIFIED:
    • 理论没有限制,可以无限大。

MeasureSpec相关推荐

  1. Android自定义View:MeasureSpec的真正意义与View大小控制

    自定义View是Android开发中最普通的需求,灵活控制View的尺寸是开发者面临的第一个问题,比如,为什么明明使用的是WRAP_CONTENT却跟MATCH_PARENT表现相同.在处理View尺 ...

  2. ANDROID自定义视图——onMeasure流程,MeasureSpec详解

    简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3. ...

  3. ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记

    原文链接 http://sparkyuan.me/ 转载请注明出处 View的绘制流程是从ViewRoot的performTraversals方法開始的.它经过measure.layout和draw三 ...

  4. MeasureSpec学习 - 转

    2019独角兽企业重金招聘Python工程师标准>>> 在自定义View和ViewGroup的时候,我们经常会遇到int型的 MeasureSpec 来表示一个组件的大小,这个变量里 ...

  5. MeasureSpec学习

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

  6. MeasureSpec源码解读

    文章目录 MeasureSpec的源码 MeasureSpec与LayoutParams 今天来讲讲MeasureSpec吧.因为他与View的测量流程相关性很大,只有正确的理解了MeasureSpe ...

  7. 布局使用DrawerLayout ,出现DrawerLayout must be measured with MeasureSpec.EXACTLY.异常

    布局中用DrawerLayout布局, 并且使用layout_width="match_parent",layout_height="match_parent" ...

  8. 关于DrawerLayout must be measured with MeasureSpec.EXACTLY问题解决办法

    在使用DrawerLayout布局时将其宽高设置为自动填充屏幕后便会出现 DrawerLayout must be measured with MeasureSpec.EXACTLY.异常. 关于这个 ...

  9. Android自学之路,DrawerLayout must be measured with MeasureSpec.EXACTLY.错误

    我们在引用android.support.v4包中的drawerlayout时经常出现这种错误 DrawerLayout must be measured with MeasureSpec.EXACT ...

  10. DrawerLayout must be measured with MeasureSpec.EXACTLY.

    DrawerLayout must be measured with MeasureSpec.EXACTLY. 必须给定大小 不能用wrap

最新文章

  1. 腾讯、网易邮箱设置POP3客户端代理
  2. GDCM:gdcm::Anonymizer的测试程序
  3. JAVA实验报告九异常处理_JAVA实验报告_Java异常处理
  4. 电脑故障维修判断指导大全(5)
  5. c语言实验报告7,C语言实验报告7.doc
  6. Bailian2944 单词替换【字符串流】
  7. java中的.take(),Rxjava2~take~timer~interval~buffer~filter等源码如何实现(你应该懂的)~学渣带你扣rxjava2...
  8. 《高性能mysql》之MySQL高级特性(第七章)
  9. USB-CAN模块使用
  10. wps下一步快捷键_WPS表格常用快捷键大全(非常全面)
  11. python 基于模板生成ppt_【Python3】通过模板实现PPT的自动生成
  12. Android内存管理的原理--进程管理
  13. gazebo actor
  14. 期货基本面分析:,马来西亚10月1-10日棕榈油出口量较上月同期下降17.3%,但对中国出口创一年新高
  15. 赵小楼《天道》深度解析(75)客观是对现有事实的认可,嘴上认可可不行,得心里认,否则就是自欺
  16. matlab图加上箭头和标注实例
  17. Content-Type 属性值
  18. Linux 日志查看 | cat 命令
  19. iPhone X携人脸识别改变行业,美图美妆用人脸识别改变女人
  20. 【Unity3D】button组件无法使用onclick与image组件无法改变属性(如fillAmount等)的解决办法

热门文章

  1. 【微信小程序】【云开发】【从零开发】【零成本】翻译小程序 —《阿怼翻译》0.0.1版本(有教程视频和源码)
  2. 南邮历午计算机复试题,南京邮电大学2011年考研计算机复试流程
  3. 【面试招聘】我的秋招记录——(自然语言处理-面经+感悟)
  4. Tool:Adobe Photoshop
  5. 苹果5完美越狱_Checkra1n 越狱常见问题汇总
  6. 暗影精灵3 黑苹果macOS BigSur 11.2.3 EFI
  7. 卷积神经网络的几种模型
  8. 3dmax教程技术:3dmax 怎么去掉贴图_3dmax如何去掉贴图
  9. 小程序人脸识别 图片转换成base64 上传给后台
  10. 4、python简单线性回归代码案例(完整)_Python:简单线性回归(不需要调用任何库,math都不要)...