MeasureSpec字面意思为测量的规格,他决定了view的测量过程

接下来探讨几个问题来学习MeauseSpec
1. MeasureSpec的构成
2. 如何创建MeasureSpec
3. 默认View的OnMeasure过程中对MeasureSpec的处理

一.MeasureSpec的构成

MeasureSpec代表一个32位的int值,前俩位代表SpecMode,后30位代表SpecSize.其中:SpecMode代表测量的模式,SpecSize值在某种测量模式下的规格大小。

共有三种测量模式:
1. EXACTLY: 父容器已经检测出子View所需要的精确大小,这个时候view的大小即为SpecSize的大小,他对应于布局参数中的MATCH_PARENT,或者精确大小值

2.AT_MOST: 父容器指定了一个大小,即SpecSize,子view的大小不能超过这个SpecSize的大小

3.UNSPECIFIED: 表示子View想多大都可以

二.如何创建MeasureSpec

MeasureSpec内部提供了创建MeasureSpec的方法:

public static int makeMeasureSpec(int size, int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

MeasureSpec代表一个32位的int值,前俩位代表SpecMode,后30位代表SpecSize.通过巧妙的位运算,即可通过MeasureSpec来得到SpecSize,SpecMode.

public static int getMode(int measureSpec) {return (measureSpec & MODE_MASK);}
  • 1
  • 2
  • 3
public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}
  • 1
  • 2
  • 3

系统内部是通过MeasureSpec来对view进行测量。在view测量的时候,系统会将LayoutParams在父容器的约束下创建MeasureSpec.

view的顶级View为DecorView.他的MeasureSpec有窗口的尺寸与布局参数来决定。对于普通的View,他的MesureSpec由父容器的MeasureSpec与自身的布局参数来一起决定的。

DecorView的MeasureSpec创建过程如下:

 private static int getRootMeasureSpec(int windowSize, int rootDimension) {int measureSpec;switch (rootDimension) {case ViewGroup.LayoutParams.MATCH_PARENT:// Window can't resize. Force root view to be windowSize.measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);break;case ViewGroup.LayoutParams.WRAP_CONTENT:// Window can resize. Set max size for root view.measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);break;default:// Window wants to be an exact size. Force root view to be that size.measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);break;}return measureSpec;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

其中windowSize为窗口的大小,即屏幕大小,rootDimension在DecorView中为MATCH_PARENT.故DecorView的MeasureSpec中的SpecSize为窗口大小,SpecMode的EXACTLY.

对于普通的子View来说,通过传入父容器的MeasureSpec,来计算子View的MeasureSpec。

通过measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed);
在其内部在调用getChildMeasureSpec()方法来得到MeasureSpec.

    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) {// 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 = 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;}return MeasureSpec.makeMeasureSpec(resultSize, resultMode);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

大体过程如下:
一:如果父容器的SpecMode为EXACTLY,那么在判断子view自己的布局参数.
1.如果为具体数值,或者MATCH_PARENT时候,那么子view的SpecMode为EXACTLY,SpecSize为父容器的SpecSize,为具体指时,SpecSize为具体值。
2.如果为WRAP_CONTENT时,那么子View的SpecMode为AT_MOST,SpecSize为父容器的SpecSize.表示子View最大不会超过父容器

二:如果父容器的SpecMode为AT_MOST时:
1.如果子View的测量规格为MATCH_PARENT,或者WRAP_CONTENT时,那么子View的SpecMode为AT_MOST,SpecSize为父容器的SpecSize.

2.如果子View的测量规格为精确值时,那么子View的测量规格为EXACTLY,SpecSize为具体的值。

上述的判断过程均为系统的判断过程,了解这些有益于得知开发过程中出现的情况的原因。

三. 默认View的OnMeasure过程中对MeasureSpec的处理

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
  • 1
  • 2
  • 3
  • 4

getDefaultSize的方法实现如下:

public static int getDefaultSize(int size, int measureSpec) {int result = size;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED:result = size;break;case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specSize;break;}return result;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

从上面的代码中可以看到默认onMeasure()方法中的判断逻辑为:
在SpecMode为AT_MOST,EXACTLY时,测量的大小都为SpecSize.

这里我举一个列子,比方说我们在activity的布局文件中自定义了一个View,把这个view的宽高都设置为WRAP_CONTNET时,这时我们会发现这个view充满了整个屏幕。发生这样的原因是正是上面代码逻辑判断的结果。首先这个字View的父容器为DecorView中的一个ViewGroup,他内部是有一个FrameLayout的布局。这个父容器的SpecSize为整个屏幕的大小。现在看子View,子view的布局参数为wrap_content,那么子View的MeasureSpec中的SpecSize为整个屏幕,SpecMode为AT_MOST,在getDefaultSize中,当测量模式AT_MOST,或者MATCH_PARENT时,都是直接把SpecSize的值给返回了,而这个SpecSize的值正好是整个屏幕,所以出现是view不管是设置为MATCH_PARENT,还是WRAP_CONTENT都是充满了整个屏幕

        <link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/markdown_views-ea0013b516.css"></div>

Android MeasureSpec理解相关推荐

  1. Android深入理解Fragment

    Fragment 目录 思维导图 概述 设计原因 基本使用 xml 声明 代码设置 添加没有 UI 的 fragment 生命周期 管理 Fragment 和执行事务 与 Activity 通信 常见 ...

  2. Android 深入理解 ANR 触发原理:Service

    一.概述 ANR(Application Not responding),是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过 ...

  3. Android深入理解文字绘制:FontMetrics字体测量及其TextPaint介绍

    文章目录 TextPaint介绍 一.FontMetrics 1.1 理论知识 1.2 代码验证 1.3 fontMetrics中的变量和文字的size.typeface有关 1.4 绘制居中屏幕的文 ...

  4. android:layout_with=,android – 难以理解layout_alignWithParentIfMissing

    这仅适用于使用RelativeLayout时. 如果您将元素设置为一个其他元素,则表示该元素位于该元素的左侧. 但是如果这个元素会丢失,因为你删除它,例如它将与父对齐. 举个例子 android:la ...

  5. 【Android个人理解(八)】跨应用调用不同组件的方法

    如果情景: 创建两个应用appA和appB,appA包括一个Service,此Service有一个堵塞方法每隔10秒钟产生一个随机数字,例如以下: public int getRandomInt(){ ...

  6. Android深入理解Context(三)Context完全解析

    1· Context类型 我们知道,Android应用都是使用Java语言来编写的,那么大家可以思考一下,一个Android程序和一个Java程序,他们最大的区别在哪里?划分界限又是什么呢?其实简单点 ...

  7. Android targetsdkversion理解

    一.Android gradle中常见的版本配置 compileSdkVersion 代表着编译的时候,会采用该api的规范进行代码检查和警告,但是并不会编译进apk中. targetSdkVersi ...

  8. Android 深入理解Android中的自定义属性

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/45022631: 本文出自:[张鸿洋的博客] 1.引言 对于自定义属性,大家肯定 ...

  9. intent在android中的作用,Android开发理解Intent的用途

    1.Intent的定义 Intent这个单词的意思就是"意图,目的,意向",Intent是一种运行时绑定(runtime binding)机制,它能在程序运行的过程中连接两个不同的 ...

  10. android keystore作用,Android KeyStore理解及簽名

    Android簽名概述 我們已經知道的是:Android對每一個Apk文件都會進行簽名,在Apk文件安裝時,系統會對其簽名信息進行比對,判斷程序的完整性,從而決定該Apk文件是否可以安裝,在一定程度上 ...

最新文章

  1. Python os.path路径模块中的操作方法总结
  2. c语言在键盘输入abc回车,C语言期末考试试卷子商务1111、21.doc
  3. java中spring的web支持nio,Spring WebClient NIO功能和问题域,与Spring Webflux一起使用
  4. iic总线从机仲裁_IIC协议底层原理超详细解析!示波器,逻辑分析仪多图预警
  5. RabbitMQ学习笔记:安装环境
  6. 新型APT组织正在攻击全球的政府实体
  7. 05-Servlet与内部加载机制(part1)
  8. 苹果Mac强大的采样器音源软件:Native Instruments Kontakt
  9. js实现的万年历显示
  10. 关于select和option下拉框样式问题
  11. 如何给PDF文件加密?PDF文件加密操作步骤来了
  12. 用office toll plus 安装office2019 vol版
  13. Excel读写合集:Excel读写小白从不知所措到轻松上手
  14. oracle sysdate 月份,如何将oracle中的sysdate月份值转换为数字?
  15. 03-多线程下载网图
  16. 基于神经网络的预测控制,神经网络预测系统应用
  17. 简谈python正则表达式
  18. MySQL 中的系统库之sys 系统库
  19. 8 Hibernate:关联映射(Associations)
  20. 【综合应用】基础PLS-SEM模型STATA实战

热门文章

  1. 一键清除windows远程桌面访问痕迹bat脚本
  2. 使用谷歌浏览器出现插件未就绪以及CLodop云打印服务(localhost本地)未安装启动!请下载安装包!
  3. 最简单的文件加密工具(完全免费)
  4. 如何读代码?读代码的利器---FreeMind
  5. efs+pro+for+三星android设备,【极光ROM】-【三星S8/S8+ G9550/G9500】-【V30.0 Android-PIE-TL2】...
  6. 一加6android9玩飞车掉,解锁新速度:一加6T深度评测
  7. 华为手机助手(PC)无法连接的通用解决方案
  8. 2020年ROS机器人操作系统用户官方调查
  9. Mujoco有限状态机以及轨迹追踪
  10. 北醒TF雷达在飞控F4 上的应用