measure的三个部分:预测量、布局窗口、最终测量
预测量
预测量时ViewRootImpl给mView的specSize:
1.第一次预测量时,给应用可用的最大尺寸。
2.窗口为悬浮窗的时候,给应用可用的最大尺寸。
3.其他情况,在mView的LayouParams.width/height为MATCH_PARENT或WRAP_CONTENT时,使用窗口最新尺寸,为精确值时,使用这个精确值作为specSize。(窗口最新尺寸是取自ViewRootImpl.mWinFrame:Rect,而不是ViewRootImpl.mWidth/mHeight,也不是mParams.width/height。mWinFrame保存了窗口的尺寸和位置并与WMS同步,mWidth/mHeight保存了窗口布局后的窗口尺寸,除非WMS单方面改变了窗口尺寸,否则mWidth/mHeight和mWinFrame的尺寸相同)
预测量使用measureHierarchy(),其中包含了协商算法,正在进行一次控件树测量的时performMeasure(),协商就是多次使用不同的参数调用performMeasure()。 协商只在窗口为悬浮窗(width或height指定为WRAP_CONTENT)时才执行。
4.需要进行协商的原因:想让悬浮窗布局尽可能更美观,从而再第一次预测量时限定悬浮窗的宽度。预测量阶段最多会测量三次。第一次给的specSize为小于给应用可用的最大尺寸(假设为y)的一个值,假设是x。如果测量结果不满意,则放宽宽度,第二次测量的specSize设为(x+y)/2;如果还是不满意,则再放宽,第三次测量的specSize为y。
布局窗口与最终测量
预测量是算出mView(ViewRootImpl的根View)的期望尺寸,即窗口的期望尺寸。在下一步调用IWindowSession.relayoutWindow()时,就使用此尺寸为参数,告诉WMS希望分配一块这么大的Surface,relayoutWindow()会输出返回Surface及其尺寸与位置。如果分配的Surface与期望的尺寸不一致,则需以Surface的实际尺寸作为mView的specSize重新测量,这样控件树就在给定mView的尺寸的基础上测量出每个View尺寸。因为就最终测量时没有协商余地,测量一次就完了,所以直接调用performMeasure()。
当预测量得到mView的尺寸与窗口最新尺寸不一致时才需要重新布局,只有布局窗口后,WMS分配返回的Surface的实际尺寸与布局窗口传入的期望尺寸不一致时才需要最终测量。
跟窗口相关的属性会绑定到控件树每个View的mAttachInfo中
View#getMeasuredWidth/HeightAndState():获得View的测量值和是否满意的标志的复合型int
View#getMeasuredWidth/Height():获得View的测量值,布局控件树阶段就是使用该值作为宽高
自定义View实现measure阶段:
复写protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,如果时View,则直接根据MeasureSpec和控件实际情况确定宽高,并调用setMeasuredDimension(int measuredWidth, int measuredHeight)。如果时ViewGroup,则再onMeasure中先逐个调用child.measure(int widthMeasureSpec, int heightMeasureSpec),并根据上个child的测量结果去计算下一个child的MeasureSpec。在所有child都测量完成后,根据所有child的结果去确定ViewGroup自身的最终尺寸。
MeasureSpec
由高二位的specMode和低30位的specSize组成。specMode根据自身的width/height和父控件的specMode决定。specMode
有EXACTLY、ATMOST、UNSPECIFIED.
可以通过ViewGroup的public static int getChildMeasureSpec(int spec, int padding, int childDimension)获得child的MeasureSpec。RootView(如decorView)的MeasureSpec由ViewRootImpl的private static int getRootMeasureSpec(int windowSize, int rootDimension)获得。
measuredWidth
由最高二位的state和低30位的size组成。state就是too_small或者非too_small.