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 mode
		int specMode = MeasureSpec.getMode(spec);
		//获取view的size
        int specSize = MeasureSpec.getSize(spec);

		//设置父view的size
        int 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;
				//此时赋值为EXACTLY
                resultMode = 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_MOST
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
		//当父View的spec mode为 AT_MOST 时,父View强加给一个最大的size给子view
        case MeasureSpec.AT_MOST:
		      //当用户设置了宽度值,比如xml文件里面设置了dimen值,直接用之
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                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, but our size is not fixed.
                // Constrain child to not be bigger than us.
				//此时父view的大小和子view的大小一样
                resultSize = size;
				//此时mode 为AT_MOST
                resultMode = 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_MOST
                resultMode = 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 it
                resultSize = 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
                // be
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = 0;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }

		//最后调用makeMeasureSpec方法来提供子view的spec
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

通过分析这个方法可以得出如下结论:从这个方法里可以看出父view的measureSpec在某种程度上决定了子view的measureSpec

1)不论父View 的specMode是EXACTLY、UNSPECIFIED、AT_MOST的哪一种,如果子view在xml文件里面把layout_width或者layout_height这是了具体

的dimen值、或者在代码里调用相应的方法为view的宽度或者高度设置了具体的值,那么此时widthSpec的mode 或者heightSpec的mode就是EXACLTY,size就等于layout_width或者

layout_height的值。此时子view的specMode值不受父View的specMode的影响。

2)当父View的specMode为EXACTLY的时候:父View强加给子View一个确切的大小,有如下两种情况

2.1)子View的layout_width或者layout_height设置为MATCH_PARENT的时候,子View的specMode为EXACTLY

2.2)子View的layout_width或者layout_height设置为WRAP_CONTENT的时候,子View的specMode为AT_MOST

2.3)不论子view为match_parent或者wrap_content,resultSize都等于父类的size.

3)当父view的specMode为AT_MOST的时候:父View强加给一个最大的size给子view,最大的size也就是父view的size

3.1)此时不论子view的为match_parent或者wrap_content,子view的specMode都为AT_MOST

3.2)resultSize的大小被设置为父view的大小

4)当父view的specMode为UNSPECIFIED的时候:

4.1) 此时不论子view的为match_parent或者wrap_content,子view的specMode都为UNSPECIFIED

4.3)此时reusltSize = 0

5)从对UNSPECIFIED分析可以知道,在1)情况之外的情况下,要是想让子view自己决定自己的宽度或者高度的大小,MeasureSpec.makeMeasureSpec(0,UNSPECIFIED)这么调用来获取宽度和高度

的spec

6)根据方法的参数以及方法的实现可以知道,视图的大小是由父视图和子视图共同决定的,前提是子视图没有设置具体的dimen值

可以用下表来总结上面的结论,并作为此篇博客的总结:

时间: 2024-08-28 20:30:32

MeasureSpec的简单说明的相关文章

[Android][转]Android View绘制13问13答

转自:http://www.androidchina.net/4458.html 1.view的绘制流程分几步,从哪开始?哪个过程结束以后能看到view? 答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程.draw流程结束以后就可以在屏幕上看到view了. 2.view的测量宽高和实际宽高有区别吗? 答:基本上百分之99的情况下都是可以认为没有区别的.有两种情况,有区别.第一种 就是有的时候会因为某些原因 view会多次测量,那

Android应用程序窗口View的measure过程

Android应用程序的界面是View组成的,这些View以树形结构组织,它们存在着父子关系,其中,子view位于父view里面,因此,在绘制一个Android应用程序窗口的View之前,我们首先要确定它里面的各个子view在父view里面的大小以及位置.确定各个子view在父view里面的大小以及位置的过程又称为测量过程和布局过程.测量和布局完后,就可以绘制view了. 参考上一篇文章Android应用程序窗口View的创建过程,可以知道Android应用程序窗口的根View是DecorVie

ANDROID自定义视图——onMeasure流程,MeasureSpec详解

简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3.绘制--onDraw():如何绘制这个View. 而第3步的onDraw系统已经封装的很好了,基本不用我们来操心,只需要专注到1,2两个步骤就中好了. 而这篇文章就来谈谈第一步,也是十分关键得一步:"测量(Measure)" Measure(): Measure的中文意思就是测量.所以它的

自定义实现ExpandableListView收缩的简单动画效果

以下是 ExpandableListView 收缩的简单动画效果 1 /* 2 * Copyright (C) 2014 Gary Guo 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the L

match_parent、wrap_parent、具体值 和 MeasureSpec 类中 mode 的对应关系

测试结果如下: * wrap_parent -> MeasureSpec.AT_MOST * match_parent -> MeasureSpec.EXACTLY * 具体值 -> MeasureSpec.EXACTLY 一个 view 的 onMeasure 方法最终得到的测量规格值(测量约束值)中包含的测量模式和上面不一定对的上,这是因为 onMeasure 方法中得到的测量规格值(测量约束值)是 measure 方法传过来的,父 view 在调用 measure 方法的时候可以根

android 自定义控件之简单的loading框

好吧,久不动android,感觉自己已经快是条咸鱼了,趁着这周的开发任务已完成,下周的开发计划未下来之际,来温习一下android的自定义控件,于是就有了下面这个丑陋的玩意 实现起来也是非常简单,下面直接上代码: public class RingLoading extends View { private final Context mContext; private Paint mPaint; private int outRadius; private int innerRadius; p

【Android自定义ViewGroup】不一样的轮子,巧用类变量解决冲突,像IOS那样简单的使用侧滑删除,一个控件搞定Android item侧滑删除菜单。

================================================================================== [1 序言] 侧滑删除的轮子网上有很多,最初在github上看过一个,还是ListView时代,那是一个自定义ListView 实现侧滑删除的,当初就觉得这种做法不是最佳,万一我项目里又同时有自定义ListView的需求,会增加复杂度. 写这篇文章之前又通过毒度搜了一下,排名前几的CSDN文章,都是通过自定义ListVIew和Vie

简单解决ListView和ScrollView冲突,复杂情况仅供参考

ScrollView嵌套ListView冲突问题的最优解决方案 (转) 记录学习之用 项目做多了之后,会发现其实 ScrollView嵌套ListVew或者GridView等很常用,但是你也会发现各种奇怪问题产生.根据个人经验现在列出常见问题以及代码最少最简单的解决方法. 问题一 : 嵌套在 ScrollView的 ListVew数据显示不全,我遇到的是最多只显示两条已有的数据. 解决办法:重写 ListVew或者 GridView,网上还有很多若干解决办法,但是都不好用或者很复杂. @Over

最简单也最难——如何获取到Android控件的高度

问题 如何获取一个控件的长和高,相信很多朋友第一眼看见这个问题都会觉得很简单,直接在onCreate里面调用getWidth.getMeasuredWidth不就可以获得了吗,但是,事实上是并没有简单的,不信的话,你可以去试一下,在onCreate里面,你是无法获得长宽值的,始终为0. 原因 这是为什么呢,其实熟悉view绘制流程的朋友应该一眼就看出来了,在onCreate中,我们的控件其实还并没有画好,换句话说,等onCreate方法执行完了,我们定义的控件才会被度量(measure),所以我