【原创】Android View框架总结(四)View布局流程之Measure

6
回复
751
查看
[复制链接]

12

主题

34

帖子

491

安币

攻城狮

Rank: 3Rank: 3

发表于 2016-8-15 19:47:47 | 显示全部楼层 |阅读模式
本帖最后由 逆流的鱼yuiop 于 2016-8-15 20:19 编辑

  • View树的measure流程
  • View的measures时序图
  • View布局流程之measure
    • measure过程
      • View的measure过程
      • ViewGroup的measure过程
      • FrameLayout的measure过程


View树的measure流程图如下:

View树的measure时序图:

View的measure过程

View的measure方法如下:

View.java

这是个final方法, 子类不能重写, 主要是在里面会调用onMeasure方法.

此处有一个优化, 就是把MeasureSpec保存, 下次执行measure方法时如果measureSpec不变就不执行measure流程, 除非设置了PFLAG_FORCE_LAYOUT标志, 这个标志就是在requestLayout里面设置的.
onMeasure之后会检查PFLAG_MEASURED_DIMENSION_SET标志, 也就是要求的onMeasure方法里面要调用setMeasuredDimensition方法(注意此方法设置的是尺寸不是MeasureSpec).
看默认的onMeasure方法


这里调用 setMeasuredDimension方法,默认通过getDefaultSize计算宽高. 其中 在看此方法之前,先看getSuggestedMinimumxxx()方法:


getSuggestedMinimumWidth是此View的最小尺寸, 一般是背景图片的尺寸和minWidth/minHeight属性值较大的. 当然也可以重写此方法返回自定义值.

然后看getDefaultSize()方法:

不限定大小则返回自己大小, 否则返回父容器限定的最大值.
结合上面的MeasureSpec我们知道, 如果View的LayoutParams使用wrap_content, 那么它的SpecMode是AT_MOST模式. 这种情况下此View大小是父容器的剩余空间大小, 和match_parent一致了. 因此一般情况下自定义View的子类都需要重新onMeasure方法.
再看setMeasuredDimension方法:

这个方法决定了当前View的大小,也是一个final方法,写自定义控件大小时,重写onMeasure方法,最终都会调用此方法。到此一次最基础的元素View的measure过程就完成了。

View实际是挂在decorView树上的树枝,而且measure是递归的,所以每个View都需要measure。一般能够嵌套的View一般都是ViewGroup的子类,所以在ViewGroup中定义了measureChildren, measureChild, measureChildWithMargins方法来对子视图进行测量,measureChildren内部实质只是循环调用measureChild,measureChild和measureChildWithMargins的区别就是是否把margin和padding也作为子视图的大小。下面分析ViewGroup的measure过程

ViewGroup的measure过程

ViewGroup的measure过程除了完成自己的measure, 还需要遍历左右的子元素的measure方法. 虽然ViewGroup提供了measureChildren方法遍历每个子元素进行measure, 但是不同的布局类型有不同的measure逻辑, 有的ViewGroup自身尺寸依赖子元素的尺寸(如LinearLayout), 有的部分依赖子元素尺寸(如ScrollView), 因此需要子类实现onMeasure具体过程.

measureChildren方法:

measureChild方法:

measureChildWithMargins方法:

虽然不同ViewGroup的measure流程不一致, 但是measure单个子View的逻辑基本是一样的, 就是上面的measureChildWidthMargins方法, 因此ViewGroup子类基本都使用此方法来measure子元素.

以上几个方法都会调用getChildMeasureSpec()方法:

补充 MeasureSpec.makeMeasureSpec(resultSize, resultMode),也可以看上一篇
Android View框架总结(三)View工作原理

从这个逻辑,我们可以知道child的specMode,specSize是通过其父View提供的MeasureSpec参数得到specMode和specSize,然后根据计算出来的specMode以及子View的childDimension(layout_width或layout_height)来计算自身的measureSpec,如果其本身包含子视图,则计算出来的measureSpec将作为调用其子视图measure函数的参数,同时也作为自身调用setMeasuredDimension的参数,如果其不包含子视图则默认情况下最终会调用onMeasure的默认实现,并最终调用到setMeasuredDimension。

FrameLayout的measure过程



上面方法总结如下:

  • 如果传入的SpecMode是EXACTLY, FrameLayout自身的尺寸不依赖子元素, 或者子元素是wrap_content(有AT_MOST限制), 那么一次遍历即可完成, 子元素兄弟之间没有依赖关系, 都是调用measureChildWidthMargins即可;
  • 否则需要遍历子元素measure后, 取最大的尺寸(但不超过父容器限制)为自身尺寸, 然后重新measure那些宽或高是match_parent的子元素, 再次遍历过程中SpecMode变成了EXACTLY.

第一时间获得博客更新提醒,以及更多android干货,源码分析,欢迎关注我的微信公众号,扫一扫下方二维码或者长按识别二维码,即可关注。

如果你觉得好,随手点赞,也是对笔者的肯定,也可以分享此公众号给你更多的人,原创不易


21

主题

9535

帖子

2036

安币

Android大神

Rank: 6Rank: 6

发表于 2016-8-15 19:51:44 | 显示全部楼层
相当不错,感谢无私分享精神!

92

主题

9630

帖子

1957

安币

Android大神

Rank: 6Rank: 6

发表于 2016-8-15 19:56:16 | 显示全部楼层
感谢大神~

14

主题

1万

帖子

3889

安币

码皇(巴士元老)

Rank: 8Rank: 8

发表于 2016-8-15 19:56:56 | 显示全部楼层
相当不错,感谢无私分享精神!

10

主题

9882

帖子

848

安币

代码手工艺人

Rank: 4

发表于 2016-8-15 19:57:46 | 显示全部楼层
感谢大神~

0

主题

9525

帖子

2466

安币

Android大神

Rank: 6Rank: 6

发表于 2016-8-15 20:10:44 | 显示全部楼层
感谢分享,楼主V5~

0

主题

1万

帖子

2395

安币

Android大神

Rank: 6Rank: 6

发表于 2016-8-15 20:14:40 | 显示全部楼层
mark,收藏了
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

领先的中文移动开发者社区
18620764416
7*24全天服务
意见反馈:1294855032@qq.com

扫一扫关注我们

Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 粤ICP备15117877号 )