1. Android对View的测量是半协商半强制半模糊半具体的.
    2. 测量过程中的两套尺寸体系: 
      1. [半强制] ParentView**约束ChildView: **MeasureSpec(通过measure方法传递给ChildView, MeasureSpec本身包含了两类信息: SpecMode和SpecSize): 
        1. SpecMode = EXACTLY: 给ChildView指定了具体尺寸[半具体], 保存在SpecSize中,默认ChildView应该遵循,Android原生的View基本都遵循了这个约定,当然了,你自己实现时可以不遵循,后果自负.
        2. SpecMode = AT_MOST: 约束了ChildView的最大尺寸[半模糊], 保存在SpecSize中,默认ChildView应该遵循,Android原生的View基本都遵循了这个约定,当然了,你自己实现时可以不遵循,后果自负.
        3. SpecMode = UNSPECIFIED: 对ChildView的尺寸不做约束[半模糊], ChildView完全自由发挥.
      2. [半协商] ChildView向ParentView**表达自己的意愿: **LayoutParam(ChildView自己携带,ParentView在为ChildView生成强制性MeasureSpec时应该将ChildView的LayoutParam也考虑进去,这也是一个非强制性约定,你完全可以自己实现时不考虑ChildView的LayoutParam) 
        1. MATCH_PARENT[半模糊]: ChildView想和ParentView一样大。
        2. WRAP_CONTENT[半模糊]: ChildView需要大到能够容纳自己的内容。
        3. 具体的尺寸[半具体]: ChildView已经被指定了具体的尺寸。
    3. Margin和Padding在测量过程会被考虑进去,其中: 
      • Margin来自ChildView的LayoutParams, 属于ChildView。
      • Padding来自ParentView的Padding属性, 属于ParentView。
    4. Android有一套不成文的测量规范,体现在其定义的函数和原生复合View的源码中,View/ViewGroup提供了一套函数供使用者按照Android的测量规范进行测量,Android原生的复合View很多都会使用这套函数来履行契约,在实现自定义View时,没有特殊需要,也推荐使用这套函数:

      1. ViewGroup: getChildMeasureSpec(int spec, int padding, int childDimension)

        • getChildMeasureSpec综合考虑了ChildView的LayoutParam(ChildView的自我诉求,在这个函数中是childDimension,及ChildView的LayoutParam在某个维度的尺寸参数: width/height)和ParentView的所受到的约束性MeasureSpec(ParentView的Parent施加的),以及Padding/Margin, 生成测量ChildView用的MeasureSpec: 在下面的流程中,得到SpecMode和SpecSize,然后组合成一个MeasureSpec。
        • spec = EXACTLY(ParentView已经被其Parent指定了具体尺寸): 
          • childDimension >= 0: ChildView表示自己希望某个具体尺寸,无条件尊重ChildView的意愿, SpecMode设置为EXACTLY,SpecSize设置为childDimension的数值
          • childDimension == MATCH_PARENT: ChildView表示希望和ParentView一样大, 正好现在有了ParentView的具体尺寸(因为SpecMode是EXACTLY, MeasureSpec的SpecMode设置为EXACTLY,SpecSize设置为ParentView的size。
          • childDimension == WRAP_CONTENT: ChildView表示需要大到可以容纳自己的内容,这个需求是模糊的,因为这个时候ChildView没有measure,是不知道其内容具体的尺寸的,ParentView能做的只是施加一个约束: ChildView不能比ParentView大, 因此SpecMode设置为AT_MOST, SpecSize设置为ParentView的size
        • spec = AT_MOST(ParentView的size没有被具体指定,只是被告知一个最大值): 
          • childDimension >= 0: ChildView表示自己希望某个具体尺寸, 无条件尊重ChildView的意愿, SpecMode设置为EXACTLY,SpecSize设置为childDimension的数值
          • childDimension == MATCH_PARENT: ChildView表示希望和ParentView一样大,因为ParentView此时的size是不确定的,因此也只能为ChildView施加一个约束: 既然希望和ParentView一样大,那么也需要遵循ParentView的最大尺寸约束,SpecMode设置为AT_MOST, SpecSize设置为ParentView的最大尺寸
          • childDimension == WRAP_CONTENT: ChildView表示需要大到可以容纳自己的内容,类似上面的情况,这种情况下,唯一能施加的约束是ChildView不能大于ParentView的最大尺寸(和ChildView不能大于ParentView比,打了个折扣,因为ParentView尺寸不确定,只能这样约束), SpecMode设置为AT_MOST, SpecSize设置为ParentView的最大尺寸.
        • spec = UNSPECIFIED(ParentView的尺寸没有任何约束,自由发挥): 
          • childDimension >= 0: ChildView表示自己希望某个具体尺寸,无条件尊重ChildView的意愿, SpecMode设置为EXACTLY,SpecSize设置为childDimension的数值
          • childDimension == MATCH_PARENT: ChildView表示希望和ParentView一样大, 这种情况下,ParentView自己的尺寸是模糊的,只能也告知ChildView自由发挥: SpecMode设置为AUNSPECIFIED
          • childDimension == WRAP_CONTENT: ChildView表示需要大到可以容纳自己的内容, 同样这种条件下做不了什么约束,只能告知ChildView自由发挥: SpecMode设置为AUNSPECIFIED
        • 最后将SpecMode和SpecSize组合为MeasureSpec作为对ChildView的测量约束
        • 总结来看:  
          1. 在ChildView通过LayoutParam**指定了自己具体尺寸的情况下,其诉求被无条件接受**。
          2. 在ChildView本身的诉求是模糊的情况下(MATCH_PARENT/WRAP_CONTENt), 会综合ParentView的测量约束和Padding/Margin进行综合考虑来实现约束[该约束可能是具体的,也可能是模糊的]。
          3. 约束很多时候是尽力而为,结合当前手头所有的尽量得到一个较为精准的约束(但是大多数时候得不到)。
      2. ViewGroup: measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed): 
        • ParentView基于ChildView的Padding,自己的Margin,自己被Parent施加的MeasureSpec测量约束,ChildView的LayoutParam尺寸, 调用getChildMeasureSpec函数得到一个在当前条件下遵循Android测量规范的MeasureSpec测量约束。
        • ChildView在Width和Height两个维度的MeasureSpec都按照这样的流程的得到。
        • 将得到的MeasureSpec传递给ChildView的measure函数,开始测量ChildView。
      3. ViewGroup: measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec): 
        • 基本同measureChildWithMargins
        • 只考虑ChildView的Padding,不考虑ParentView的Margin.
      4. ViewGroup: measureChildren(int widthMeasureSpec, int heightMeasureSpec) 
        • 对所有非GONE的childView调用measureChild进行符合Android测量规范的测量。
      5. View: resolveSizeAndState(int size, int measureSpec, int childMeasuredState) 
        • 在测量阶段的最终,需要调用setMeasuredDimension设定View最终的测量尺寸
        • resolveSizeAndState会综合View自己的意愿(View在经过一系列测量后想为自己设置的尺寸),Parent为其施加的测量约束 和 其ChildView在测量过程中产生的附加State信息(比如MEASURED_STATE_TOO_SMALL), 得到一个包含了state信息和尺寸的值,作为setMeasuredDimension的参数。
        • Parent约束SpecMode是UNSPECIFIED: 
          1. 表示Parent对View没有约束,那么使用View自己希望的测量结果即可。
        • Parent约束SpecMode是AT_MOST: 
          1. 如果View的测量结果超过了Parent的限制, 那么使用Parent的限制值,不过State会附加上MEASURED_STATE_TOO_SMALL向上层(一般是ViewRootImpl)告知自己对测量结果不满意
        • Parent约束SpecMode是EXACTLY: 
          1. Parent为ChildView**规定了具体尺寸,ChildView不能反抗,只能遵循,并且也不能通过State向上反馈意见,是最强制性的措施**。
          2. 上面看似是Parent压倒了ChildView的意见,但是参见getChildMeasureSpec函数, 在规范流程下,只有ChildView自己在LayoutParam要求了具体尺寸或者MATCH_PARENT才会有Parent施加EXACTLY约束, 因此,其实这里还是ChildView自己的意愿。
          3. 当然了,不过遵循这套规范流程,上面的结论是不成立的。
        • 上述流程得到不光是一个尺寸,还包括了state, ChildView的State也会被合并, 最终所有参与测量的View的State被合并传至最上层。
        • 上述State信息要通过getMeasuredWidth/HeightAndState才能获得。
        • combineMeasuredStates函数可以用来合并State
      6. View: resolveSize(int size, int measureSpec) 
        • 基本同resolveSizeAndState
        • 返回的结果中只包含Size,不包含State。
      7. View: getDefaultSize(int size, int measureSpec) 
        • 可以看作是resolveSize的一个退化实现, View的onMeasure默认实现使用了这个函数。
        • 在Parent约束为AT_MOST/EXACTLY时,使用SpecSize作为自己的测量结果。
        • 在Parent约束为UNSPECIFIED时,使用自己的测量结果。
    5. measureChildWithMargins等函数的存在不代表不能直接调用ChildView的measure函数,在measureChildWithMargins生成的MeasureSpec**不满足你的需求时**,完全可以自己生成MeasureSpec然后调用ChildView的measure函数。

    6. 最小宽度/高度: Android规范测量流程会建议在测量过程中考虑View的最小宽度/高度

      1. getSuggestedMinimumWidth/Height提供了View的最小宽度/高度
      2. 最小宽度/高度一方面取决于setMinimumWidth/Height设置的值
      3. 另一方面取决于Background(Drawable)的getMinimumWidth/Height
      4. View的默认实现是上面两者取最大者,自定义View有特殊需求可以重写这个函数。
      5. 最小宽度/高度是规范但不强制,如果你自定义一个根本不考虑这些的View, 也没关系,但是代价是ChildView的setMinimumWidth/Height()函数不能生效, Background也可能显示不全等等。
    7. View在一次整个View体系测量历程中,可能会被测量复数次(measure函数被调用数次),这是由上层View来决定的,每种View都有不同的测量逻辑。

      1. 比如FrameLayout,在某些情况下,会measure两次指定了MATCH_PARENT的ChildView。
    8. 测量的起点是ViewRootImpl, 整个测量的简化流程(一个极度简单理想化的模型: View体系是 RootView -> P1 View -> P2 View -> Child View): 
      1. ViewRootImpl: performTraversals() ->
      2. ViewRootImpl: measureHierarchy() ->
      3. ViewRootImpl: performMeasure() ->
      4. RootView: measure()被施加测量约束进行测量 ->
      5. P1 View: measure()被施加测量约束进行测量 ->
      6. P2 View: measure()被施加测量约束进行测量 ->
      7. Child View: measure()被施加测量约束进行测量 ->
      8. Child View: measure() 测量完毕,调用setMeasuredDimension()确定自己尺寸。
      9. P2 View: measure() 根据Child View的测量结果做进一步的处理运算,可能重新测量Child View, 最后得出一个适合自己的尺寸,调用setMeasuredDimension()确定自己尺寸。
      10. P1 View: measure() 根据P2 View的测量结果做进一步的处理运算,可能重新测量P2 View, 最后得出一个适合自己的尺寸,调用setMeasuredDimension()确定自己尺寸。
      11. RootView: measure() 根据P1 View的测量结果做进一步的处理运算,可能重新测量P1 View, 最后得出一个适合自己的尺寸,调用setMeasuredDimension()确定自己尺寸。
      12. ViewRootImpl: 整个View体系的测量完成,但不排除后续会重新发起测量(比如检查State发现有View不满足当前测量结果,或者和WMS协商后发现测量结果和窗口尺寸有冲突)

https://blog.csdn.net/fyfcauc/article/details/54288343

Android Measure 体系简单总结的更多相关文章

  1. Android View体系(十)自定义组合控件

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

  2. Android View体系(九)自定义View

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

  3. Android View体系(八)从源代码解析View的layout和draw流程

    相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...

  4. Android四层体系架构

    Application应用层 应用是用Java语言编写的运行在虚拟机上的程序,即图中最上层的蓝色部分.手机的上层应用其实,Google最开始时就在Android系统中捆绑了一些核心应用比如e-mail ...

  5. Canvas 知识体系简单总结

    Canvas 知识体系简单总结 标签(空格分隔): HTML5 Canvas 本文原创,如需转载,请注明出处 前言 知识点零零散散,一个上午整理了一下,内容不多,方便记忆. 本文不是教程,如需教程移步 ...

  6. Android:PopupWindow简单弹窗改进版

    Android:PopupWindow简单弹窗 继续上一节的内容,改进一下,目标是点击菜单后把菜单收缩回去并且切换内容,我使用的是PopupWindow+RadioGroup public class ...

  7. Android View体系(一)视图坐标系

    前言 Android View体系是界面编程的核心,他的重要性不亚于Android四大组件,在这个系列中我会陆续讲到View坐标系.View的滑动.View的事件分发等文章来逐步介绍Android V ...

  8. Android View体系(四)从源码解析Scroller

    在Android View体系(二)实现View滑动的六种方法这篇文章中我们讲到了用Scroller来实现View的滑动,所以这篇文章我们就不介绍Scroller是如何使用的了,本篇就从源码来分析下S ...

  9. Android.mk文件简单分析

    Android.mk文件简单分析 一个Android.mk文件用来向编译系统描写叙述须要编译的源码.详细来说:该文件是GNUMakefile的一小部分.会被编译系统解析一次或多次. 能够在每个Andr ...

随机推荐

  1. android意图传參数(四)

    一.依照向导创建一个project,layout的activity_main.xml文件内容例如以下: <RelativeLayout xmlns:android="http://sc ...

  2. react 项目实战(六)提取布局组件

    重复代码是混乱的根源!,本篇文章我们来继续消灭重复代码. 目标 细心的同学应该能发现:每一个Page组件(/src/pages下的组件)的render方法都拥有相似的jsx结构,比如: render ...

  3. 用Swift语言和Sprite Kit复制微信飞机大战游戏

    先上GitHub链接: https://github.com/songrotek/PlaneWar.git 接下来略微解说一下! 这个程序还有点Bug,见谅! 1 说明 游戏採用了Sprite kit ...

  4. JavaScript基础 -- 常见DOM树操作

    1.创建并增加元素节点 <ul id="ul"> <li>1</li> <li>2</li> <li>3&l ...

  5. python 时区

    Python中的时区处理  http://tech.glowing.com/cn/dealing-with-timezone-in-python/ Python时区设置方法与pytz查询时区教程_py ...

  6. 获取浏览器弹窗alert、自定义弹窗以及其操作

    web自动化测试第10步:获取浏览器弹窗alert.自定义弹窗以及其操作 - CSDN博客 http://blog.csdn.net/ccggaag/article/details/76573857 ...

  7. GeHost powershell

    PS C:\Users\clu\Desktop> [System.Net.Dns] | Get-Member -Static | Format-Table -AutoSize TypeName: ...

  8. NSubstitute

    https://github.com/nsubstitute/NSubstitute http://nsubstitute.github.io/help/creating-a-substitute/

  9. Swift-AES之加密解密

    什么是AES 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代 ...

  10. Code First:Data Anotation (2)

    示例一 本例演示与索引有关的内容,模型: using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAn ...