初始入门,没有看具体的源码,只是用到时结合几篇文章得出自己目前的理解。有很多错误的地方,以后在学习研究内核剖析时在深入研究
参考:
(1)最外层根视图得到MeasureSpec的流程
对于每一个View,包括DecorView,都持有一个MeasureSpec,而该MeasureSpec则保存了该View的尺寸规格。那么,对于DecorView来说,它已经是顶层view了,没有父容器,那么它的MeasureSpec怎么来的呢?
步骤:
View系统的绘制流程会从ViewRoot的
performTraversals()
方法中开始
——>调用
getRootMeasureSpec(int windowSize, int rootDimension)
方法去获取widthMeasureSpec和heightMeasureSpec的值(是根视图的MeasureSpec,详细实现于3),用于传递给measure()的参数- windowSize→赋值:desiredWindowWidth或desiredWindowHeight。是屏幕的尺寸,相当于specSize都是等于windowSize
- rootDimension→赋值:lp.width或lp.height,相当于MATCH_PARENT(根视图全屏)
——>
getRootMeasureSpec()里使用
MeasureSpec.makeMeasureSpec()
方法来组装一个MeasureSpec。具体分类:- rootDimension:MATCH_PARENT→MeasureSpec的specMode:EXACTLY
- rootDimension:WRAP_CONTENT→MeasureSpec的specMode:AT_MOST
- 并且MATCH_PARENT和WRAP_CONTENT时的specSize都是等于windowSize的
——>
以上就是得到了一份DecorView根视图的MeasureSpec:childWidthMeasureSpec和childHeightMeasureSpec
接着就执行performMeasure里的
performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)
方法,调用其中的mView.measure
(mView就是DecorView)也就是说,从顶级View开始了测量流程,那么我们直接进入measure流程。
注:由于DecorView继承自FrameLayout,是PhoneWindow的一个内部类,而FrameLayout没有measure方法,因此调用的是父类View的measure方法。
我们知道,整棵View树的根节点是DecorView,它是一个FrameLayout,所以它是一个ViewGroup,所以整棵View树的测量是从一个ViewGroup对象的measure方法开始的。
(有疑问)
measure流程可以分为两类:
- ViewGroup的测量流程
- View的测量流程
我们先看View类中measure和onMeasure函数:
(2)View的测量流程
view类中的measure()方法,无法在子类中去重写这个方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec);
——>onMeasure()方法回调,才是真正去测量并设置View大小的地方。可以默认实现,也可以自定义实现
- 2.1. onMeasure的默认实现很简单,源码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); } /* onMeasure()默认会调用getDefaultSize(int size, int measureSpec)方法来获取视图的大小 1、系统默认行为 2、MeasureSpec.UNSPECIFIED时,返回size(理解:具体布局数值,则measure()没有必要了) 3、specMode等于AT_MOST或EXACTLY时,就返回specSize(specSize = MeasureSpec.getSize(measureSpec)) */
- 2.2.当然也可以重载onMeasure,并调用setMeasuredDimension来设置任意大小的布局,但一般不这么做,因为这种做法太“专政”。
3.之后会在onMeasure()方法中调用setMeasuredDimension()
方法来设定测量出的大小,即把测量结果保存起来,具体保存在mMeasuredWidth和mMeasuredHeight中。结束一次measure过程。//
(3)ViewGroup的测量流程
ViewGroup中定义了一个measureChildren()方法来去测量(遍历所有)子视图的大小
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec);
——>
逐个调用measureChild()方法来测量相应子视图的大小。
protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)
或protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed)
对于ViewGroup的子类而言(非ViewGroup的View而言,通过调用上面默认的measure——>onMeasure,即可完成View的测量),以上一次遍历就是对父视图提供的measureSpec参数进行了调整(结合自身的LayoutParams参数),具体通过函数getChildMeasureSpec来进行参数调整,然后再来调用child.measure()函数。
——>- measureChildren过程中最困难的一部分,为child计算MeasureSpec。该方法为每个child的每个维度(宽、高)计算正确的MeasureSpec。目标就是把当前viewgroup的MeasureSpec和child的LayoutParams结合起来,生成最合理的结果。
public static int getChildMeasureSpec(int spec, int padding, int childDimension)
具体实现:通过父视图的measureSpec、自身的lp参数(layout_width和layout_height中定义的,childDimension包括:具体数值、LayoutParams.MATCH_PARENT、LayoutParams.WRAP_CONTENT)和size(自身大小 int size = Math.max(0, specSize - padding); )来计算自身的measureSpec
——> - 接着循环到4,直到遍历完所有子视图。测量始于DecorView,通过不断的遍历子View的measure方法,根据ViewGroup的MeasureSpec及子View的LayoutParams来决定子View的MeasureSpec,进一步获取子View的测量宽高,然后逐层返回,不断保存ViewGroup的测量宽高。
- 总之:视图大小的控制是由父视图、布局文件、以及视图本身共同完成的,父视图会提供给子视图参考的大小,而开发人员可以在XML文件中指定视图的大小,然后视图本身会对最终的大小进行拍板。
- 探究:EXACTLY、AT_MOST与MATCH_PARENT和WRAP_CONTENT的关系??