MeasureSpec的简单说明

注:Spec为specification的缩写,以为规格或者说明书的意思(英语不好,专门用英语翻译软件翻译了一下)。所以顾名思义该类的所以就是定义View的测量规格或者测量规则。这个类是View里面的嵌套内部类,提供了三个对外公开的static变量UNSPECIFIED,EXACTLY,AT_MOST,,这三个变量统称为specMode,对于一个View来说它的宽和高各有属于自己的specMode,至于其具体作用后面会有说说明。MeasureSpec提供了三个方法

1)makeMeasureSpec(int size,int mode):size参数由程序员自己设定,mode必须是specMode的三个值中的一个

2)getMode(int measureSpec):见名知意,方法返回specMode的三个值中的一个,注意方法参数measureSpec,这个参数的值是怎么得来的呢?正是由makeMeasureSpec方法计算出来的

3)getSize(int measureSpec):获取View的大小,方法参数的值同样是由makeMeasureSpec计算的出来的。

注意:看android源码的时候有一个小技巧:凡是参数的名字中带有spec后缀的参数,该值都是由makeMeasureSpec来计算出来的,这也是代码中良好命名习惯带来的好处之一。

android的绘制流程中,第一个流程是Measure测量流程,而MeasureSpec在测量的过程中起到了至关重要的作用,不论是自定义View还是android自带的View,只要重写了onMearsure方法都能发现MeasureSpec的影子。测量过程中View类提供了measure方法,在该方法中调用了onMeasure方法,先看看这两份方法的签名measure(int widthMeasureSpec, int heightMeasureSpec)、onMeasure(int widthMeasureSpec, int heightMeasureSpec),onMeasure方法中的参数是由measure方法直接传过去的;但是measure方法的参数是由谁来传过来的呢?还是那句话,看看方法名的后缀都有spec,这肯定和MeasureSpec方法有关了,确定得说他们的值就是有makeMeasureSpec方法来决定的。事实上正是如此:在measure的时候measure方法是由ViewRoot的performTraversals来调用的,在该方法中调用了getRootMeasureSpec方法,详见这位大神的博客。

上面一直强调参数命名参数后缀为spec的方法跟MeasureSpec的关系密切,下面就顺着这个思路沿着View的继承机构挑几个类来查看分析MeasureSpec具体作用的体现。View类就有onMeasure和Measure方法,上面已经做了简单的说明,下面具体看看ViewGroup的方法,在viewGroup里面是没有重写onMeasure方法的,换句话说方法参数带有spec后缀的只有measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec),measureChildren(int widthMeasureSpec, int heightMeasureSpec)和measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed)。在这里挑measureChild方法来进行说明:

protected void measureChild(View child, int parentWidthMeasureSpec,int parentHeightMeasureSpec) {final LayoutParams lp = child.getLayoutParams();final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight, lp.width);final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom, lp.height);//执行child的measure流程child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }对于以上代码要注意点就是方法参数parentWidthMeasureSpec和parentHeightMeasureSpec说明是父View的Spec,并且在代码里调用了getChildMeasureSpec方法。下面就看看getChildMeasureSpec的方法具体都干了些什么好事。

方法参数说明:padding:当前父view的内边距 childDimension: 子视图想要绘制的准确大小,其值可以是LayoutParams对象的width或者height变量。也就ch是xml文件中layout_height或者layout_width的值,可以是 match_parent和wrap_content,也可以是具体的值

spec:可以是父View经过makeMeasureSpec计算的值,也可以传递用户自己通过makeMeasureSpec计算的值(如果在你自己的代码中调用getChildMeasureSpec的话) public static int getChildMeasureSpec(int spec, int padding, int childDimension) {//1.获取specMode,注意此时是父类的spec modeint specMode = MeasureSpec.getMode(spec);//获取view的sizeint specSize = MeasureSpec.getSize(spec);//设置父view的sizeint size = Math.max(0, specSize – padding);//因为spec有size和mode共同构成,所以定义了如下两个变量,经过处理后供makeMeasureSpec使用int resultSize = 0;int resultMode = 0;//解析specMode并做相应的处理switch (specMode) {// Parent has imposed an exact size on us//当父View的spec mode为 EXACTLY 时,父View强加给子View一个确切的大小case MeasureSpec.EXACTLY://当用户设置了宽度值,比如xml文件里面设置了dimen值,直接用之if (childDimension >= 0) {resultSize = childDimension;//在子view自己设置了size的情况下,比如xml文件里面layout_width或者layout_height设置了具体的dimen值resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {//如果子View layout_width或者layout_height为mactch_parent// Child wants to be our size. So be it.//子view的大小与父view的大小一样resultSize = size;//此时赋值为EXACTLYresultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.WRAP_CONTENT) {//如果子View layout_width或者layout_height为wrap_content// Child wants to determine its own size. It can't be// bigger than us.//子view的大小由自己来决定,但是不能超过父View的大小resultSize = size;//此时赋值为At_MOSTresultMode = MeasureSpec.AT_MOST;}break;// Parent has imposed a maximum size on us//当父View的spec mode为 AT_MOST 时,父View强加给一个最大的size给子viewcase MeasureSpec.AT_MOST://当用户设置了宽度值,比如xml文件里面设置了dimen值,直接用之if (childDimension >= 0) {// Child wants a specific size… so be itresultSize = childDimension;//在子view自己设置了size的情况下,比如xml文件里面layout_width或者layout_height设置了具体的dimen值resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {//如果子View layout_width或者layout_height为mactch_parent// Child wants to be our size, but our size is not fixed.// Constrain child to not be bigger than us.//此时父view的大小和子view的大小一样resultSize = size;//此时mode 为AT_MOSTresultMode = MeasureSpec.AT_MOST;} else if (childDimension == LayoutParams.WRAP_CONTENT) {//如果子View layout_width或者layout_height为wrap_content// Child wants to determine its own size. It can't be// bigger than us.//子view的大小由自己来决定,但是不能超过父View的大小resultSize = size;//此时mode 为AT_MOSTresultMode = MeasureSpec.AT_MOST;}break;// Parent asked to see how big we want to be//在父View的specMode为UNSPECIFIED,让子view决定自己有多大case MeasureSpec.UNSPECIFIED://当用户设置了宽度值,比如xml文件里面设置了dimen值,直接用之if (childDimension >= 0) {// Child wants a specific size… let him have itresultSize = childDimension;//在子view自己设置了size的情况下,比如xml文件里面layout_width或者layout_height设置了具体的dimen值resultMode = MeasureSpec.EXACTLY;} else if (childDimension == LayoutParams.MATCH_PARENT) {// Child wants to be our size… find out how big it should// beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;} else if (childDimension == LayoutParams.WRAP_CONTENT) {// Child wants to determine its own size…. find out how// big it should beresultSize = 0;resultMode = MeasureSpec.UNSPECIFIED;}break;}//最后调用makeMeasureSpec方法来提供子view的specreturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);}通过分析这个方法可以得出如下结论:从这个方法里可以看出父view的measureSpec在某种程度上决定了子view的measureSpec

功夫不负有心人。

MeasureSpec的简单说明

相关文章:

你感兴趣的文章:

标签云: