android学习——MeasureSpec介绍及使用

来源:互联网 发布:银行从业考试题库软件 编辑:程序博客网 时间:2024/06/05 20:25

转载:http://www.cnblogs.com/nanxiaojue/p/3536381.html?utm_source=tuicool


一、MeasureSpc类说明  SDK的介绍:MeasureSpc类封装了父View传递给子View的布局(layout)要求。每个MeasureSpc实例代表宽度或者高度

它有三种模式:
①、UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
②、EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
③、AT_MOST(至多),子元素至多达到指定大小的值。

常用的三个函数:
static int getMode(int measureSpec) : 根据提供的测量值(格式),提取模式(上述三个模式之一)
static int getSize(int measureSpec) : 根据提供的测量值(格式),提取大小值(这个大小也就是我们通常所说的大小)
static int makeMeasureSpec(int size,int mode) : 根据提供的大小值和模式,创建一个测量值(格式)

MeasureSpc类源码分析 其为View.java类的内部类,路径:frameworksbasecorejavaandroidviewView.java

复制代码
 1 public class View implements ... { 2 ... 3 public static class MeasureSpec { 4 private static final int MODE_SHIFT = 30; //移位位数为30 5 //int类型占32位,向右移位30位,该属性表示掩码值,用来与size和mode进行"&"运算,获取对应值。 6 private static final int MODE_MASK = 0x3 << MODE_SHIFT; 7  //向右移位30位,其值为00 + (30位0)  , 即 0x0000(16进制表示)   8     public static final int UNSPECIFIED = 0 << MODE_SHIFT;   9     //向右移位30位,其值为01 + (30位0)  , 即0x1000(16进制表示)  10     public static final int EXACTLY     = 1 << MODE_SHIFT;  11     //向右移位30位,其值为02 + (30位0)  , 即0x2000(16进制表示)  12     public static final int AT_MOST     = 2 << MODE_SHIFT;  13 14     //创建一个整形值,其高两位代表mode类型,其余30位代表长或宽的实际值。可以是WRAP_CONTENT、MATCH_PARENT或具体大小exactly size  15     public static int makeMeasureSpec(int size, int mode) {  16         return size + mode;  17     }  18     //获取模式  ,与运算  19     public static int getMode(int measureSpec) {  20         return (measureSpec & MODE_MASK);  21     }  22     //获取长或宽的实际值 ,与运算  23     public static int getSize(int measureSpec) {  24         return (measureSpec & ~MODE_MASK);  25     }  26 27 }  28 ...  
复制代码
MeasureSpec类的处理思路是:
右移运算,使int 类型的高两位表示模式的实际值,其余30位表示其余30位代表长或宽的实际值----可以是WRAP_CONTENT、MATCH_PARENT或具体大小exactly size。通过掩码MODE_MASK进行与运算 “&”,取得模式(mode)以及长或宽(value)的实际值。

MeasureSpec.makeMeasureSpec方法,实际上这个方法很简单:

1 public static int makeMeasureSpec(int size, int mode) {2             return size + mode;3      }

其用法如下:

1 int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);2 int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);3 ssidtext.measure(w, h);4 int width =ssidtext.getMeasuredWidth();5 int height =ssidtext.getMeasuredHeight();

 

二、measure过程详解

UI框架开始绘制时,皆是从ViewRoot.java类开始绘制的:

ViewRoot类简要说明: 任何显示在设备中的窗口,例如:Activity、Dialog等,都包含一个ViewRoot实例,该类主要用来与远端 WindowManagerService交互以及控制(开始/销毁)绘制。

1、开始UI绘制 , 具体绘制方法则是:

复制代码
//开始View绘制流程  private void performTraversals(){      ...      //这两个值我们在后面讨论时,在回过头来看看是怎么赋值的。现在只需要记住其值MeasureSpec.makeMeasureSpec()构建的。      int childWidthMeasureSpec; //其值由MeasureSpec类构建 , makeMeasureSpec      int childHeightMeasureSpec;//其值由MeasureSpec类构建 , makeMeasureSpec      // Ask host how big it wants to be      host.measure(childWidthMeasureSpec, childHeightMeasureSpec);      ...  }  ...  
复制代码

2、调用measure()方法去做一些前期准备 measure()方法原型定义在View.java类中,final修饰符修饰,其不能被重载:

复制代码
 1 public class View implements ... { 2 ... 3 /** 4 * This is called to find out how big a view should be. The parent 5 * supplies constraint information in the width and height parameters. 6 * 7 * @param widthMeasureSpec Horizontal space requirements as imposed by the 8 * parent 9 * @param heightMeasureSpec Vertical space requirements as imposed by the10 * parent11 * @see #onMeasure(int, int)12 */13 public final void measure(int widthMeasureSpec, int heightMeasureSpec) {14 //判断是否为强制布局,即带有“FORCE_LAYOUT”标记 以及 widthMeasureSpec或heightMeasureSpec发生了改变15 if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||16 widthMeasureSpec != mOldWidthMeasureSpec ||17 heightMeasureSpec != mOldHeightMeasureSpec) {18 // first clears the measured dimension flag  19         //清除MEASURED_DIMENSION_SET标记   ,该标记会在onMeasure()方法后被设置  20         mPrivateFlags &= ~MEASURED_DIMENSION_SET;   21 22         // measure ourselves, this should set the measured dimension flag back  23         // 1、 测量该View本身的大小 ; 2 、 设置MEASURED_DIMENSION_SET标记,否则接写来会报异常。  24         onMeasure(widthMeasureSpec, heightMeasureSpec);  25 26         // flag not set, setMeasuredDimension() was not invoked, we raise  27         // an exception to warn the developer  28         if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {  29             throw new IllegalStateException("onMeasure() did not set the"  30                     + " measured dimension by calling" + " setMeasuredDimension()");  31         }  32 33         mPrivateFlags |= LAYOUT_REQUIRED;  //下一步是layout了,添加LAYOUT_REQUIRED标记  34     }  35 36     mOldWidthMeasureSpec = widthMeasureSpec;   //保存值  37     mOldHeightMeasureSpec = heightMeasureSpec; //保存值  38 }  39 ...  
复制代码
参数widthMeasureSpec和heightMeasureSpec 由父View构建,表示父View给子View的测量要求。其值地构建如下:measure()方法显示判断是否需要重新调用设置改View大小,即调用onMeasure()方法,然后操作两个标识符:①、重置MEASURED_DIMENSION_SET : onMeasure()方法中,需要添加该标识符,否则,会报异常;②、添加LAYOUT_REQUIRED : 表示需要进行layout操作。最后,保存当前的widthMeasureSpec和heightMeasureSpec值。

 3、调用onMeasure()方法去真正设置View的长宽值,其默认实现为:

复制代码
 1 //设置该View本身地大小 2 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 3 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 4 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); 5 } 6  7  8 //@param size参数一般表示设置了android:minHeight属性或者该View背景图片的大小值 9 public static int getDefaultSize(int size, int measureSpec) {10 int result = size;11 int specMode = MeasureSpec.getMode(measureSpec);12 int specSize = MeasureSpec.getSize(measureSpec);13 14 //根据不同的mode值,取得宽和高的实际值。  15   switch (specMode) {  16   case MeasureSpec.UNSPECIFIED:  //表示该View的大小父视图未定,设置为默认值  17       result = size;  18       break;  19   case MeasureSpec.AT_MOST:      //表示该View的大小由父视图指定了  20   case MeasureSpec.EXACTLY:  21       result = specSize;  22       break;  23   }  24   return result;  25 }26 27 //获得设置了android:minHeight属性或者该View背景图片的大小值, 最为该View的参考值28 protected int getSuggestedMinimumWidth() {29 int suggestedMinWidth = mMinWidth; // android:minHeight30 if (mBGDrawable != null) { // 背景图片对应地Width。  31       final int bgMinWidth = mBGDrawable.getMinimumWidth();  32       if (suggestedMinWidth < bgMinWidth) {  33           suggestedMinWidth = bgMinWidth;  34       }  35   }  36 37   return suggestedMinWidth; 38 }39 //设置View在measure过程中宽和高40 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {41 mMeasuredWidth = measuredWidth;42 mMeasuredHeight = measuredHeight;43 mPrivateFlags |= MEASURED_DIMENSION_SET;  //设置了MEASURED_DIMENSION_SET标记  44 }
复制代码
主要功能就是根据该View属性(android:minWidth和背景图片大小)和父View对该子View的"测量要求",设置该View的 mMeasuredWidth 和 mMeasuredHeight 值。这儿只是一般的View类型地实现方法。一般来说,父View,也就是ViewGroup类型,都需要在重写onMeasure()方法,遍历所有子View,设置每个子View的大小。基本思想如下:遍历所有子View,设置每个子View的大小。伪代码表示为:
复制代码
 1 //某个ViewGroup类型的视图 2 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 3 //必须调用super.ononMeasure()或者直接调用setMeasuredDimension()方法设置该View大小,否则会报异常。 4 super.onMeasure(widthMeasureSpec , heightMeasureSpec) 5  6 //遍历每个子View 7 for(int i = 0 ; i < getChildCount() ; i++){ 8 View child = getChildAt(i); 9 //调用子View的onMeasure,设置他们的大小。childWidthMeasureSpec , childHeightMeasureSpec ?10 child.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec);11 }12 }
复制代码

 

如何去设置每个子View的大小,基本思想也如同我们之前描述的思想:遍历所有子View,设置每个子View的大小。
复制代码
 1 //widthMeasureSpec 和 heightMeasureSpec 表示该父View的布局要求 2 //遍历每个子View,然后调用measureChild()方法去实现每个子View大小 3 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 4 final int size = mChildrenCount; 5 final View[] children = mChildren; 6 for (int i = 0; i < size; ++i) { 7 final View child = children[i]; 8 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { // 不处于 “GONE” 状态 9 measureChild(child, widthMeasureSpec, heightMeasureSpec);10 }11 }12 }13 //测量每个子View高宽时,清楚了该View本身的边距大小,即android:padding属性 或android:paddingLeft等属性标记14 protected void measureChild(View child, int parentWidthMeasureSpec,15 int parentHeightMeasureSpec) {16 final LayoutParams lp = child.getLayoutParams(); // LayoutParams属性17 //设置子View的childWidthMeasureSpec属性,去除了该父View的边距值 mPaddingLeft + mPaddingRight18 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,19 mPaddingLeft + mPaddingRight, lp.width);20 //设置子View的childHeightMeasureSpec属性,去除了该父View的边距值 mPaddingTop + mPaddingBottom21 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,22 mPaddingTop + mPaddingBottom, lp.height);23 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
复制代码
measureChildren()方法:遍历所有子View,调用measureChild()方法去设置该子View的属性值。measureChild()  方法   : 获取特定子View的widthMeasureSpec、heightMeasureSpec,调用measure()方法设置子View的实际宽高值。getChildMeasureSpec()就是获取子View的widthMeasureSpec、heightMeasureSpec值。
复制代码
 1 // spec参数 表示该父View本身所占的widthMeasureSpec 或 heightMeasureSpec值 2 // padding参数 表示该父View的边距大小,见于android:padding属性 或android:paddingLeft等属性标记 3 // childDimension参数 表示该子View内部LayoutParams属性的值,可以是wrap_content、match_parent、一个精确指(an exactly size), 4 // 例如:由android:width指定等。 5 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 6 int specMode = MeasureSpec.getMode(spec); //获得父View的mode 7 int specSize = MeasureSpec.getSize(spec); //获得父View的实际值 8 int size = Math.max(0, specSize - padding); //父View为子View设定的大小,减去边距值,   9 10 int resultSize = 0;    //子View对应地 size 实际值 ,由下面的逻辑条件赋值  11 int resultMode = 0;    //子View对应地 mode 值 , 由下面的逻辑条件赋值  12 13 switch (specMode) {  14 // Parent has imposed an exact size on us  15 //1、父View是EXACTLY的 !  16 case MeasureSpec.EXACTLY:   17     //1.1、子View的width或height是个精确值 (an exactly size)  18     if (childDimension >= 0) {            19         resultSize = childDimension;         //size为精确值  20         resultMode = MeasureSpec.EXACTLY;    //mode为 EXACTLY 。  21     }   22     //1.2、子View的width或height为 MATCH_PARENT/FILL_PARENT   23     else if (childDimension == LayoutParams.MATCH_PARENT) {  24         // Child wants to be our size. So be it.  25         resultSize = size;                   //size为父视图大小  26         resultMode = MeasureSpec.EXACTLY;    //mode为 EXACTLY 。  27     }   28     //1.3、子View的width或height为 WRAP_CONTENT  29     else if (childDimension == LayoutParams.WRAP_CONTENT) {  30         // Child wants to determine its own size. It can't be  31         // bigger than us.  32         resultSize = size;                   //size为父视图大小  33         resultMode = MeasureSpec.AT_MOST;    //mode为AT_MOST 。  34     }  35     break;  36 37 // Parent has imposed a maximum size on us  38 //2、父View是AT_MOST的 !      39 case MeasureSpec.AT_MOST:  40     //2.1、子View的width或height是个精确值 (an exactly size)  41     if (childDimension >= 0) {  42         // Child wants a specific size... so be it  43         resultSize = childDimension;        //size为精确值  44         resultMode = MeasureSpec.EXACTLY;   //mode为 EXACTLY 。  45     }  46     //2.2、子View的width或height为 MATCH_PARENT/FILL_PARENT  47     else if (childDimension == LayoutParams.MATCH_PARENT) {  48         // Child wants to be our size, but our size is not fixed.  49         // Constrain child to not be bigger than us.  50         resultSize = size;                  //size为父视图大小  51         resultMode = MeasureSpec.AT_MOST;   //mode为AT_MOST  52     }  53     //2.3、子View的width或height为 WRAP_CONTENT  54     else if (childDimension == LayoutParams.WRAP_CONTENT) {  55         // Child wants to determine its own size. It can't be  56         // bigger than us.  57         resultSize = size;                  //size为父视图大小  58         resultMode = MeasureSpec.AT_MOST;   //mode为AT_MOST  59     }  60     break;  61 62 // Parent asked to see how big we want to be  63 //3、父View是UNSPECIFIED的 !  64 case MeasureSpec.UNSPECIFIED:  65     //3.1、子View的width或height是个精确值 (an exactly size)  66     if (childDimension >= 0) {  67         // Child wants a specific size... let him have it  68         resultSize = childDimension;        //size为精确值  69         resultMode = MeasureSpec.EXACTLY;   //mode为 EXACTLY  70     }  71     //3.2、子View的width或height为 MATCH_PARENT/FILL_PARENT  72     else if (childDimension == LayoutParams.MATCH_PARENT) {  73         // Child wants to be our size... find out how big it should  74         // be  75         resultSize = 0;                        //size为0! ,其值未定  76         resultMode = MeasureSpec.UNSPECIFIED;  //mode为 UNSPECIFIED  77     }   78     //3.3、子View的width或height为 WRAP_CONTENT  79     else if (childDimension == LayoutParams.WRAP_CONTENT) {  80         // Child wants to determine its own size.... find out how  81         // big it should be  82         resultSize = 0;                        //size为0! ,其值未定  83         resultMode = MeasureSpec.UNSPECIFIED;  //mode为 UNSPECIFIED  84     }  85     break;  86 }  87 //根据上面逻辑条件获取的mode和size构建MeasureSpec对象。  88 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  
复制代码

 

每个View大小的设定都事由其父View以及该View共同决定的。但这只是一个期望的大小,每个View在测量时最终大小的设定是由setMeasuredDimension()最终决定的。因此,最终确定一个View的“测量长宽“是由以下几个方面影响:
1、父View的MeasureSpec属性;2、子View的LayoutParams属性 ;3、setMeasuredDimension()或者其它类似设定 mMeasuredWidth 和 mMeasuredHeight 值的方法。
1 //设置View在measure过程中宽和高2 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {3 mMeasuredWidth = measuredWidth;4 mMeasuredHeight = measuredHeight;5 mPrivateFlags |= MEASURED_DIMENSION_SET;  //设置了MEASURED_DIMENSION_SET标记  6 }

 

 


0 0
原创粉丝点击