MeasureSpec
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相关推荐
- Android自定义View:MeasureSpec的真正意义与View大小控制
自定义View是Android开发中最普通的需求,灵活控制View的尺寸是开发者面临的第一个问题,比如,为什么明明使用的是WRAP_CONTENT却跟MATCH_PARENT表现相同.在处理View尺 ...
- ANDROID自定义视图——onMeasure流程,MeasureSpec详解
简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3. ...
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
原文链接 http://sparkyuan.me/ 转载请注明出处 View的绘制流程是从ViewRoot的performTraversals方法開始的.它经过measure.layout和draw三 ...
- MeasureSpec学习 - 转
2019独角兽企业重金招聘Python工程师标准>>> 在自定义View和ViewGroup的时候,我们经常会遇到int型的 MeasureSpec 来表示一个组件的大小,这个变量里 ...
- MeasureSpec学习
在自定义View和ViewGroup的时候,我们经常会遇到int型的MeasureSpec来表示一个组件的大小,这个变量里面不仅有组件的尺寸大小,还有大小的模式. 这个大小的模式,有点难以理解.在系统 ...
- MeasureSpec源码解读
文章目录 MeasureSpec的源码 MeasureSpec与LayoutParams 今天来讲讲MeasureSpec吧.因为他与View的测量流程相关性很大,只有正确的理解了MeasureSpe ...
- 布局使用DrawerLayout ,出现DrawerLayout must be measured with MeasureSpec.EXACTLY.异常
布局中用DrawerLayout布局, 并且使用layout_width="match_parent",layout_height="match_parent" ...
- 关于DrawerLayout must be measured with MeasureSpec.EXACTLY问题解决办法
在使用DrawerLayout布局时将其宽高设置为自动填充屏幕后便会出现 DrawerLayout must be measured with MeasureSpec.EXACTLY.异常. 关于这个 ...
- Android自学之路,DrawerLayout must be measured with MeasureSpec.EXACTLY.错误
我们在引用android.support.v4包中的drawerlayout时经常出现这种错误 DrawerLayout must be measured with MeasureSpec.EXACT ...
- DrawerLayout must be measured with MeasureSpec.EXACTLY.
DrawerLayout must be measured with MeasureSpec.EXACTLY. 必须给定大小 不能用wrap
最新文章
- 腾讯、网易邮箱设置POP3客户端代理
- GDCM:gdcm::Anonymizer的测试程序
- JAVA实验报告九异常处理_JAVA实验报告_Java异常处理
- 电脑故障维修判断指导大全(5)
- c语言实验报告7,C语言实验报告7.doc
- Bailian2944 单词替换【字符串流】
- java中的.take(),Rxjava2~take~timer~interval~buffer~filter等源码如何实现(你应该懂的)~学渣带你扣rxjava2...
- 《高性能mysql》之MySQL高级特性(第七章)
- USB-CAN模块使用
- wps下一步快捷键_WPS表格常用快捷键大全(非常全面)
- python 基于模板生成ppt_【Python3】通过模板实现PPT的自动生成
- Android内存管理的原理--进程管理
- gazebo actor
- 期货基本面分析:,马来西亚10月1-10日棕榈油出口量较上月同期下降17.3%,但对中国出口创一年新高
- 赵小楼《天道》深度解析(75)客观是对现有事实的认可,嘴上认可可不行,得心里认,否则就是自欺
- matlab图加上箭头和标注实例
- Content-Type 属性值
- Linux 日志查看 | cat 命令
- iPhone X携人脸识别改变行业,美图美妆用人脸识别改变女人
- 【Unity3D】button组件无法使用onclick与image组件无法改变属性(如fillAmount等)的解决办法
热门文章
- 【微信小程序】【云开发】【从零开发】【零成本】翻译小程序 —《阿怼翻译》0.0.1版本(有教程视频和源码)
- 南邮历午计算机复试题,南京邮电大学2011年考研计算机复试流程
- 【面试招聘】我的秋招记录——(自然语言处理-面经+感悟)
- Tool:Adobe Photoshop
- 苹果5完美越狱_Checkra1n 越狱常见问题汇总
- 暗影精灵3 黑苹果macOS BigSur 11.2.3 EFI
- 卷积神经网络的几种模型
- 3dmax教程技术:3dmax 怎么去掉贴图_3dmax如何去掉贴图
- 小程序人脸识别 图片转换成base64 上传给后台
- 4、python简单线性回归代码案例(完整)_Python:简单线性回归(不需要调用任何库,math都不要)...