View的绘制过程从Activity.setContentView开始经过如下方法:
Activity.setContentView—>PhoneWindow.setContentView—>ViewRootImpl.requestLayout—>ViewRootImpl.scheduleTraversals—>ViewRootImpl.TraversalRunnable—>ViewRootImpl. doTraversal—>ViewRootImpl. performTraversals—>ViewRootImpl.performMeasure—>ViewRootImpl. performLayout—>ViewRootImpl. performDraw
Measure
测量View的大小,从ViewRootImpl.measureHierarchy开始,计算各个控件显示需要多大的尺寸。
其中getRootMeasureSpec用来获取根布局的MeasureSpec,MeasureSpec封装了从父容器传递给子容器的布局要求,而不是父容器对子容器的布局要求,更精确的说法是,MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过计算得出的一个针对子View的测量要求,这个测量要求就是MeasureSpec。类似于下图,由一个32位的整型将mode和size包装起来。
MeasureSpec一共有三种模式,UNSPECIFIED(mode=0),父容器对于子容器大小没有任何限制,子容器想要多大就多大;EXACTLY(mode=-1):父容器已经为子容器设置了尺寸,子容器无论想要多大空间,都应该服从父容器指定的边界;AT_MOST(mode=-2):子容器可以是父容器指定边界内的任意大小。
根布局直接根据自己width和height属性计算MeasureSpec,MATCH_PARENT说明父布局的大小就是窗口大小,mode就是EXACTLY;WRAP_CONTENT说明根布局的最大尺寸就是窗口的尺寸,mode为AT_MOST。
在计算出Root的MeasureSpec后就通过performMeasure来调用DectorView的measure方法,该方法计算出View的大小,参数是父View对它的宽高约束,实际的测量工作是在onMeasure方法中进行的,对于DectorView就是调用FrameLayout的onMeasure方法。
FrameLayout是ViewGroup的子类,有个View[]类型的成员变量mChildren,在上图的源码中,首先调用measureChildWithMargin方法对所有子View进行一遍策略,计算所有子View的最大宽度和高度。寄过一系列计算后得到maxHeight和maxWidth。measureChildWithMargin是ViewGroup的方法,如下所示,通过调用View.measure方法。
在measure child的时候,需要传入对子view的MeasureSpec限制,通过getChildMeasureSpec可以计算ViewGroup对子View的Spec限制,计算方式如下,根据父View的MeasureSpec和子View的LayoutParams生成子View的MeasureSpec的过程,子View的LayoutParam表示子View期待的大小。
以上代码分为以下几种情况:
1、父View的MeasureSpec为EXACTLY
如果childDimension>0,说明给子View指定了具体的大小,那么子View的mode为EXACTLY,size为childDimension;如果childDimension=MATH_PARENT,那么子View就是父View的Size,所以子View的Mode为EXACTLY,Size就是父View的Size;如果childDimension=WRAP_CONTENT,那么子View的最大Size为父View的Size,所以子View的Mode为AT_MOST,Size就是父View的Size
2、父View的MeasureSpec为AT_MOST
表示父View限制了子View最大为Size,如果childDimension>0,说明给子View指定了具体的大小,那么子View的Mode为EXACTLY,Size为childDimension;如果childDimension=MATH_PARENT,那么子View想和父View一样大,Mode就是AT_MOST,Size就是父View的Size;如果childDimension=WRAP_CONTENT,子View想要自己决定尺寸,但不能比父View大,Mode就是AT_MOST,Size就是父View的Size。
child.measure过程如下:
在调用了measureChildWithMargin方法后,获取到子View的MeasureSpec,接下来就调用子View的measure,并将MeasureSpec传进入, 最终调用onMeasure方法,并通过setMeasureDimension进行设置。
在Measure结束后就运行ViewRootImpl.performLayout,最后运行onLayout;之后运行performDraw,运行View的onDraw方法。