前言

MeasureSpec 很大程度上决定了一个View尺寸规格,是因为这个过程还受父容器的影响,因为父容器影响View 的MeasureSpec的创建过程。在测量过程中,系统会将View的LayoutParams 根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个MeasureSpec测量出View 的最终宽/高

MeasureSpec 构建

MeasureSpec 代表了一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpcSize(在某种测量模式下的规格大小)

源码:
public static class MeasureSpec{private static final int MODE_SHIFT = 30;private static final int MODE_MASK  = 0x3 << MODE_SHIFT;/** @hide */@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})@Retention(RetentionPolicy.SOURCE)public @interface MeasureSpecMode {}/*** Measure specification mode: The parent has not imposed any constraint* on the child. It can be whatever size it wants.*/public static final int UNSPECIFIED = 0 << MODE_SHIFT;/*** Measure specification mode: The parent has determined an exact size* for the child. The child is going to be given those bounds regardless* of how big it wants to be.*/public static final int EXACTLY     = 1 << MODE_SHIFT;/*** Measure specification mode: The child can be as large as it wants up* to the specified size.*/public static final int AT_MOST     = 2 << MODE_SHIFT;/*** Creates a measure specification based on the supplied size and mode.** The mode must always be one of the following:* <ul>*  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>*  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>*  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>* </ul>** <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's* implementation was such that the order of arguments did not matter* and overflow in either value could impact the resulting MeasureSpec.* {@link android.widget.RelativeLayout} was affected by this bug.* Apps targeting API levels greater than 17 will get the fixed, more strict* behavior.</p>** @param size the size of the measure specification* @param mode the mode of the measure specification* @return the measure specification based on size and mode*/public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,@MeasureSpecMode int mode) {if (sUseBrokenMakeMeasureSpec) {return size + mode;} else {return (size & ~MODE_MASK) | (mode & MODE_MASK);}}/*** Like {@link #makeMeasureSpec(int, int)}, but any spec with a mode of UNSPECIFIED* will automatically get a size of 0. Older apps expect this.** @hide internal use only for compatibility with system widgets and older apps*/@UnsupportedAppUsagepublic static int makeSafeMeasureSpec(int size, int mode) {if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {return 0;}return makeMeasureSpec(size, mode);}/*** Extracts the mode from the supplied measure specification.** @param measureSpec the measure specification to extract the mode from* @return {@link android.view.View.MeasureSpec#UNSPECIFIED},*         {@link android.view.View.MeasureSpec#AT_MOST} or*         {@link android.view.View.MeasureSpec#EXACTLY}*/@MeasureSpecModepublic static int getMode(int measureSpec) {//noinspection ResourceTypereturn (measureSpec & MODE_MASK);}/*** Extracts the size from the supplied measure specification.** @param measureSpec the measure specification to extract the size from* @return the size in pixels defined in the supplied measure specification*/public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);}static int adjust(int measureSpec, int delta) {final int mode = getMode(measureSpec);int size = getSize(measureSpec);if (mode == UNSPECIFIED) {// No need to adjust size for UNSPECIFIED mode.return makeMeasureSpec(size, UNSPECIFIED);}size += delta;if (size < 0) {Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +") spec: " + toString(measureSpec) + " delta: " + delta);size = 0;}return makeMeasureSpec(size, mode);}/*** Returns a String representation of the specified measure* specification.** @param measureSpec the measure specification to convert to a String* @return a String with the following format: "MeasureSpec: MODE SIZE"*/public static String toString(int measureSpec) {int mode = getMode(measureSpec);int size = getSize(measureSpec);StringBuilder sb = new StringBuilder("MeasureSpec: ");if (mode == UNSPECIFIED)sb.append("UNSPECIFIED ");else if (mode == EXACTLY)sb.append("EXACTLY ");else if (mode == AT_MOST)sb.append("AT_MOST ");elsesb.append(mode).append(" ");sb.append(size);return sb.toString();}}

小结: MeasureSpec 通过将SpecMode 和SpecSize 打包成一个int值 来避免过多的对象内存分配。SpecMode 和SpecSize 是一个int 值,一组SpecMode 和SpecSize 可以打包为一个MeasureSpec,而一个MeasureSpec可以通过解包的形式来得出其原始的SpecMode 和SpecSize。

MeasureSpec 分类

SpecMode 有三类:

  • UNSPECIFIED

父容器不对View 有任何限制,要多大有多大,这种情况一般用于系统内部,表示一种测量状态

  • EXACTLY

父容器已经检测出View 所需要的精确大小,这个时候View 的最终大小就是SpecSize所指定的值。它对应于LayoutParam 中的match_parent 和具体的数值这2种模式

  • AT_MOST

父容器指定了一个可用大小即 SpecSize ,View 的大小不能大于这个值,具体是什么值要看不同View 的具体实现,它对应于LayoutParam 中的wrap_content。

MeasureSpec和LayoutParams 的对应关系

系统内部通过MeasureSpec来进行View 的测量,但是正常情况下使用View 指定 MeasureSpec。在View 测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec 来确定View 测量后的宽/高。

注意 :MeasureSpec不是唯一由LayoutParams决定,LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View 的宽/高。


// ViewGroup源码中measureChild 为子View构造MeasureSpec 构建过程
protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {final LayoutParams lp = child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom, lp.height);child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}......// 该方法 根据父容器的MeasureSpec 同时结合子View本身的LayoutParams 来确定子元素的MeasureSpecpublic static int getChildMeasureSpec(int spec, int padding, int childDimension) {int specMode = MeasureSpec.getMode(spec);// 父容器大小int specSize = MeasureSpec.getSize(spec);// padding 父容器中已占用空间大小, size 为子元素可用空间大小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;}//noinspection ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);}

对于顶级View(即DecorView)和普通View来说,MeasureSpec的转换过程不同

  • 对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定。
  • 对于普通View, 其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。

总结

  • 当View 采用固定宽/高的时候,不管父容器MeasureSpec是什么,View的MeasureSpec都是精确模式并且大小遵循LayoutParams 中的大小。
  • 当View 的宽/高是match_parent时,如果父容器的模式是精准模式,那么View也是精准模式并且其大小是父容器的剩余空间;如果父容器是最大模式,那么View也是最大模式并且其大小不会超过父容器的剩余空间。
  • 当View 的宽/高是wrap_content时,不管父容器是的模式是精准还是最大化,View 的模式总是最大化并且大小不会超过父容器的剩余空间。
  • UNSPECIFD模式主要用于系统内部多次Measure 的情形。

Android 自定义控件基础:MeasureSpec相关推荐

  1. Android 自定义控件基础:measure过程

    measure过程要分为两种情况:如果是原始的View,需要通过measure 方法就完成其测量过程:如果是ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子元素的measure方法, ...

  2. android自定义流式布局思路,Android 自定义控件基础-流式布局

    什么是流式布局?其实我们在平时遇到过,只是有可能叫不出它的名字. 如图: 如上图,就是一个流式布局的样式. &esmp;这里,将记录一下怎么实现这个功能.其实实现这个功能的方法,就是自定义Vi ...

  3. 【Android 自定义控件】2.画布的基础使用

    [Android 自定义控件]2.画布的基础使用 1.设置画布的背景颜色 2.画圆形 基本语法 参数说明 3.画直线 单条直线: 基本语法 参数说明 多条直线: 基本语法 参数说明 4.画点 单个点 ...

  4. Android自定义控件开发系列(零)——基础原理篇

    在后边的文章中发现在说Android自定义时,有时候要重复解释很多东西,所以想想返回来增加一篇"基础原理篇",直接进入正题吧-- 首先的问题是:在Android项目开发中,什么时候 ...

  5. android 自定义控件及动画基础 一

    Android 自定义控件及手势动画基础 第一章 浅谈自定义控件 文章目录 Android 自定义控件及手势动画基础 前言 一.浅谈自定义View的形态有几种? 二.如何学习自定义View 1.多阅读 ...

  6. Android绘制自定义控件,Android自定义控件绘制基本图形基础入门

    本文讲述绘制android自定义各种图形效果,为自定义控件的入门篇 相关视频链接: android自定义控件系列 android视频全系列 绘制点–这个控件只需要在布局中引用或者代码中new 即可,下 ...

  7. Android自定义控件之流式布局

    效果图: 一.首先创建我 们的自定义流式布局 public class FlowLayoutView extends ViewGroup {public FlowLayoutView(Context ...

  8. 我的新书《Android自定义控件入门与实战》出版啦

    前言:当你回首往事时,不以虚度年华而悔恨,不以碌碌无为而羞耻,那你就可以骄傲的跟自己讲,你不负此生 [Android自定义控件入门与实战]勘误:https://blog.csdn.net/harvic ...

  9. android 自定义时钟,Android自定义控件之圆形时钟(续)

    在上篇文章中,我向大家介绍了如何通过自定义View一步步画出一个漂亮的圆形时钟.如果你还没看的话,我不建议你接着往下看,因为这篇文章是接着上篇的文章,如果直接看的话可能会不知所云,所以还是建议你先看一 ...

最新文章

  1. 算法基础知识科普:8大搜索算法之二分搜索
  2. Service Manger的初始化分析
  3. python sanic部署_Sanic框架配置操作分析
  4. 英特尔nuc能代替主机吗_制砂机生产的沙子可靠吗?能代替天然沙子吗?
  5. 一致性哈希算法的基本原理
  6. 合法的python变量名import_python 环境变量和import模块导入方法(详解)
  7. 【ERP】如何根据系统币种(扩展)精度来动态产生数字的格式(FORMAT MASK)
  8. Spring Data JPA初使用 *****重要********
  9. 为什么在极端恶劣环境下不能使用商用以太网交换机?
  10. Docker 网络命名空间
  11. java怎么从后天往前台传参_Java后台解析前台的get中文请求
  12. MFC 程序大小及控件自适应不同的屏幕分辨率
  13. 思科接入层交换机故障
  14. 十大排序算法(Top 10 Sorting Algorithms)
  15. [lua]紫猫lua教程-命令宝典-L1-01-07. table表
  16. Excel一键取消合并单元格并向下填充相同的内容
  17. 软负载均衡和F5负载均衡(硬负载均衡)区别
  18. 【翻译论文】An Architecture Combining Convolutional Neural Network (CNN) and Support Vector Machine (SVM)
  19. win7安全模式如何打开计算机管理,Win7安全模式怎么进?Win7进入安全模式方法
  20. ColorOS 13流畅吗?看OPPO在系统上做了哪些升级?

热门文章

  1. 阿里云虚机配置免费https证书
  2. codeforces 978D 模拟思维
  3. 激励视频如何带动有奖猜歌答题小游戏的发展?
  4. win7插了耳机还是外放_地铁禁止外放10天后,我的耳朵还是阵亡了
  5. 第一部3D片源《阿修罗》首映礼在移动电影院上线,颜丹晨白裙助阵
  6. java aio_深入理解Java AIO(一)—— Java AIO的简单使用
  7. sqlserver关于mirror镜像的总结
  8. 输入起始时间,第几周,周几,自动计算出日期
  9. html表格边框空心,各种表格样式示例及代码
  10. java servlet是单例吗_SpringMVC中DispatchServlet是单例还是多例(附源码分析)