Android Measure 体系简单总结
- Android对View的测量是半协商半强制半模糊半具体的.
- 测量过程中的两套尺寸体系:
- [半强制] ParentView**约束ChildView: **MeasureSpec(通过measure方法传递给ChildView, MeasureSpec本身包含了两类信息: SpecMode和SpecSize):
- SpecMode = EXACTLY: 给ChildView指定了具体尺寸[半具体], 保存在SpecSize中,默认ChildView应该遵循,Android原生的View基本都遵循了这个约定,当然了,你自己实现时可以不遵循,后果自负.
- SpecMode = AT_MOST: 约束了ChildView的最大尺寸[半模糊], 保存在SpecSize中,默认ChildView应该遵循,Android原生的View基本都遵循了这个约定,当然了,你自己实现时可以不遵循,后果自负.
- SpecMode = UNSPECIFIED: 对ChildView的尺寸不做约束[半模糊], ChildView完全自由发挥.
- [半协商] ChildView向ParentView**表达自己的意愿: **LayoutParam(ChildView自己携带,ParentView在为ChildView生成强制性MeasureSpec时应该将ChildView的LayoutParam也考虑进去,这也是一个非强制性约定,你完全可以自己实现时不考虑ChildView的LayoutParam)
- MATCH_PARENT[半模糊]: ChildView想和ParentView一样大。
- WRAP_CONTENT[半模糊]: ChildView需要大到能够容纳自己的内容。
- 具体的尺寸[半具体]: ChildView已经被指定了具体的尺寸。
- [半强制] ParentView**约束ChildView: **MeasureSpec(通过measure方法传递给ChildView, MeasureSpec本身包含了两类信息: SpecMode和SpecSize):
- Margin和Padding在测量过程会被考虑进去,其中:
- Margin来自ChildView的LayoutParams, 属于ChildView。
- Padding来自ParentView的Padding属性, 属于ParentView。
Android有一套不成文的测量规范,体现在其定义的函数和原生复合View的源码中,View/ViewGroup提供了一套函数供使用者按照Android的测量规范进行测量,Android原生的复合View很多都会使用这套函数来履行契约,在实现自定义View时,没有特殊需要,也推荐使用这套函数:
- 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的测量约束
- 总结来看:
- 在ChildView通过LayoutParam**指定了自己具体尺寸的情况下,其诉求被无条件接受**。
- 在ChildView本身的诉求是模糊的情况下(MATCH_PARENT/WRAP_CONTENt), 会综合ParentView的测量约束和Padding/Margin进行综合考虑来实现约束[该约束可能是具体的,也可能是模糊的]。
- 约束很多时候是尽力而为,结合当前手头所有的尽量得到一个较为精准的约束(但是大多数时候得不到)。
- 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。
- ViewGroup: measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec):
- 基本同measureChildWithMargins
- 只考虑ChildView的Padding,不考虑ParentView的Margin.
- ViewGroup: measureChildren(int widthMeasureSpec, int heightMeasureSpec)
- 对所有非GONE的childView调用measureChild进行符合Android测量规范的测量。
- 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:
- 表示Parent对View没有约束,那么使用View自己希望的测量结果即可。
- Parent约束SpecMode是AT_MOST:
- 如果View的测量结果超过了Parent的限制, 那么使用Parent的限制值,不过State会附加上MEASURED_STATE_TOO_SMALL向上层(一般是ViewRootImpl)告知自己对测量结果不满意
- Parent约束SpecMode是EXACTLY:
- Parent为ChildView**规定了具体尺寸,ChildView不能反抗,只能遵循,并且也不能通过State向上反馈意见,是最强制性的措施**。
- 上面看似是Parent压倒了ChildView的意见,但是参见getChildMeasureSpec函数, 在规范流程下,只有ChildView自己在LayoutParam要求了具体尺寸或者MATCH_PARENT才会有Parent施加EXACTLY约束, 因此,其实这里还是ChildView自己的意愿。
- 当然了,不过遵循这套规范流程,上面的结论是不成立的。
- 上述流程得到不光是一个尺寸,还包括了state, ChildView的State也会被合并, 最终所有参与测量的View的State被合并传至最上层。
- 上述State信息要通过getMeasuredWidth/HeightAndState才能获得。
- combineMeasuredStates函数可以用来合并State
- View: resolveSize(int size, int measureSpec)
- 基本同resolveSizeAndState
- 返回的结果中只包含Size,不包含State。
- View: getDefaultSize(int size, int measureSpec)
- 可以看作是resolveSize的一个退化实现, View的onMeasure默认实现使用了这个函数。
- 在Parent约束为AT_MOST/EXACTLY时,使用SpecSize作为自己的测量结果。
- 在Parent约束为UNSPECIFIED时,使用自己的测量结果。
- ViewGroup: getChildMeasureSpec(int spec, int padding, int childDimension)
measureChildWithMargins等函数的存在不代表不能直接调用ChildView的measure函数,在measureChildWithMargins生成的MeasureSpec**不满足你的需求时**,完全可以自己生成MeasureSpec然后调用ChildView的measure函数。
最小宽度/高度: Android规范测量流程会建议在测量过程中考虑View的最小宽度/高度
- getSuggestedMinimumWidth/Height提供了View的最小宽度/高度
- 最小宽度/高度一方面取决于setMinimumWidth/Height设置的值
- 另一方面取决于Background(Drawable)的getMinimumWidth/Height
- View的默认实现是上面两者取最大者,自定义View有特殊需求可以重写这个函数。
- 最小宽度/高度是规范但不强制,如果你自定义一个根本不考虑这些的View, 也没关系,但是代价是ChildView的setMinimumWidth/Height()函数不能生效, Background也可能显示不全等等。
View在一次整个View体系测量历程中,可能会被测量复数次(measure函数被调用数次),这是由上层View来决定的,每种View都有不同的测量逻辑。
- 比如FrameLayout,在某些情况下,会measure两次指定了MATCH_PARENT的ChildView。
- 测量的起点是ViewRootImpl, 整个测量的简化流程(一个极度简单理想化的模型: View体系是 RootView -> P1 View -> P2 View -> Child View):
- ViewRootImpl: performTraversals() ->
- ViewRootImpl: measureHierarchy() ->
- ViewRootImpl: performMeasure() ->
- RootView: measure()被施加测量约束进行测量 ->
- P1 View: measure()被施加测量约束进行测量 ->
- P2 View: measure()被施加测量约束进行测量 ->
- Child View: measure()被施加测量约束进行测量 ->
- Child View: measure() 测量完毕,调用setMeasuredDimension()确定自己尺寸。
- P2 View: measure() 根据Child View的测量结果做进一步的处理运算,可能重新测量Child View, 最后得出一个适合自己的尺寸,调用setMeasuredDimension()确定自己尺寸。
- P1 View: measure() 根据P2 View的测量结果做进一步的处理运算,可能重新测量P2 View, 最后得出一个适合自己的尺寸,调用setMeasuredDimension()确定自己尺寸。
- RootView: measure() 根据P1 View的测量结果做进一步的处理运算,可能重新测量P1 View, 最后得出一个适合自己的尺寸,调用setMeasuredDimension()确定自己尺寸。
- ViewRootImpl: 整个View体系的测量完成,但不排除后续会重新发起测量(比如检查State发现有View不满足当前测量结果,或者和WMS协商后发现测量结果和窗口尺寸有冲突)
https://blog.csdn.net/fyfcauc/article/details/54288343
Android Measure 体系简单总结的更多相关文章
- Android View体系(十)自定义组合控件
相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...
- Android View体系(九)自定义View
相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...
- Android View体系(八)从源代码解析View的layout和draw流程
相关文章 Android View体系(一)视图坐标系 Android View体系(二)实现View滑动的六种方法 Android View体系(三)属性动画 Android View体系(四)从源 ...
- Android四层体系架构
Application应用层 应用是用Java语言编写的运行在虚拟机上的程序,即图中最上层的蓝色部分.手机的上层应用其实,Google最开始时就在Android系统中捆绑了一些核心应用比如e-mail ...
- Canvas 知识体系简单总结
Canvas 知识体系简单总结 标签(空格分隔): HTML5 Canvas 本文原创,如需转载,请注明出处 前言 知识点零零散散,一个上午整理了一下,内容不多,方便记忆. 本文不是教程,如需教程移步 ...
- Android:PopupWindow简单弹窗改进版
Android:PopupWindow简单弹窗 继续上一节的内容,改进一下,目标是点击菜单后把菜单收缩回去并且切换内容,我使用的是PopupWindow+RadioGroup public class ...
- Android View体系(一)视图坐标系
前言 Android View体系是界面编程的核心,他的重要性不亚于Android四大组件,在这个系列中我会陆续讲到View坐标系.View的滑动.View的事件分发等文章来逐步介绍Android V ...
- Android View体系(四)从源码解析Scroller
在Android View体系(二)实现View滑动的六种方法这篇文章中我们讲到了用Scroller来实现View的滑动,所以这篇文章我们就不介绍Scroller是如何使用的了,本篇就从源码来分析下S ...
- Android.mk文件简单分析
Android.mk文件简单分析 一个Android.mk文件用来向编译系统描写叙述须要编译的源码.详细来说:该文件是GNUMakefile的一小部分.会被编译系统解析一次或多次. 能够在每个Andr ...
随机推荐
- android意图传參数(四)
一.依照向导创建一个project,layout的activity_main.xml文件内容例如以下: <RelativeLayout xmlns:android="http://sc ...
- react 项目实战(六)提取布局组件
重复代码是混乱的根源!,本篇文章我们来继续消灭重复代码. 目标 细心的同学应该能发现:每一个Page组件(/src/pages下的组件)的render方法都拥有相似的jsx结构,比如: render ...
- 用Swift语言和Sprite Kit复制微信飞机大战游戏
先上GitHub链接: https://github.com/songrotek/PlaneWar.git 接下来略微解说一下! 这个程序还有点Bug,见谅! 1 说明 游戏採用了Sprite kit ...
- JavaScript基础 -- 常见DOM树操作
1.创建并增加元素节点 <ul id="ul"> <li>1</li> <li>2</li> <li>3&l ...
- python 时区
Python中的时区处理 http://tech.glowing.com/cn/dealing-with-timezone-in-python/ Python时区设置方法与pytz查询时区教程_py ...
- 获取浏览器弹窗alert、自定义弹窗以及其操作
web自动化测试第10步:获取浏览器弹窗alert.自定义弹窗以及其操作 - CSDN博客 http://blog.csdn.net/ccggaag/article/details/76573857 ...
- GeHost powershell
PS C:\Users\clu\Desktop> [System.Net.Dns] | Get-Member -Static | Format-Table -AutoSize TypeName: ...
- NSubstitute
https://github.com/nsubstitute/NSubstitute http://nsubstitute.github.io/help/creating-a-substitute/
- Swift-AES之加密解密
什么是AES 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代 ...
- Code First:Data Anotation (2)
示例一 本例演示与索引有关的内容,模型: using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAn ...