转载请注明出处
http://www.cnblogs.com/crashmaker/p/3549365.html
From crash_coder linguowu
linguowu0622@gamil.com
前言:html
经过Android 自定义View及其在布局文件中的使用示例和Android 自定义View及其在布局文件中的使用示例(二),咱们知道了如何使用自定义的View,以及Android绘制View的理论基础,其包含三个过程,测量View大小(经过onMeasure()方法实现),计算View位置(经过onLayout()方法实现),最后开始绘制(经过onDraw()方法实现),本篇,咱们将结合Android 4.4.2_r1源码详细分析测量过程的具体实现.java
在第一篇里,咱们提供了一个自定义的View的源代码,如今引用一下该代码与测量相关的部分:android
1 @Override 2 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 3 setMeasuredDimension(measureWidth(widthMeasureSpec), 4 measureHeight(heightMeasureSpec)); 5 } 6 7 /** 8 * Determines the width of this view 9 * 10 * @param measureSpec 11 * A measureSpec packed into an int 12 * @return The width of the view, honoring constraints from measureSpec 13 */ 14 private int measureWidth(int measureSpec) { 15 int result = 0; 16 int specMode = MeasureSpec.getMode(measureSpec); 17 int specSize = MeasureSpec.getSize(measureSpec); 18 19 if (specMode == MeasureSpec.EXACTLY) { 20 // We were told how big to be 21 result = specSize; 22 } else { 23 // Measure the text 24 result = (int) mTextPaint.measureText(mText) + getPaddingLeft() 25 + getPaddingRight(); 26 if (specMode == MeasureSpec.AT_MOST) { 27 // Respect AT_MOST value if that was what is called for by 28 // measureSpec 29 result = Math.min(result, specSize); 30 } 31 } 32 33 return result; 34 } 35 36 /** 37 * Determines the height of this view 38 * 39 * @param measureSpec 40 * A measureSpec packed into an int 41 * @return The height of the view, honoring constraints from measureSpec 42 */ 43 private int measureHeight(int measureSpec) { 44 int result = 0; 45 int specMode = MeasureSpec.getMode(measureSpec); 46 int specSize = MeasureSpec.getSize(measureSpec); 47 48 mAscent = (int) mTextPaint.ascent(); 49 if (specMode == MeasureSpec.EXACTLY) { 50 // We were told how big to be 51 result = specSize; 52 } else { 53 // Measure the text (beware: ascent is a negative number) 54 result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop() 55 + getPaddingBottom(); 56 if (specMode == MeasureSpec.AT_MOST) { 57 // Respect AT_MOST value if that was what is called for by 58 // measureSpec 59 result = Math.min(result, specSize); 60 } 61 } 62 return result; 63 }
咱们能够看到:protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)是一个override的方法,它接收两个参数,经过字面意思,咱们知道,这两个参数分别为宽度测量规格,高度测量规格,此时,咱们会有一个疑问,这两个参数是从哪里来的?这个疑问我们先记下来,给它编个号:Q01,暂时略过,到本文下一部分,咱们就知道它的前因后果了.接着,咱们来看onMeasure方法在本地的实现:
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
咱们跟进setMeasuredDimension(int,int)方法,看看它到底都作了些什么事情:c#
由于咱们自定义的View是继承自View,因此咱们进入View.java(源码位置:/frameworks/base/core/java/android/view/View.java)去看看有没有这个方法:api
16575 /** 16576 * <p>This method must be called by {@link #onMeasure(int, int)} to store the 16577 * measured width and measured height. Failing to do so will trigger an 16578 * exception at measurement time.</p> 16579 * 16580 * @param measuredWidth The measured width of this view. May be a complex 16581 * bit mask as defined by {@link #MEASURED_SIZE_MASK} and 16582 * {@link #MEASURED_STATE_TOO_SMALL}. 16583 * @param measuredHeight The measured height of this view. May be a complex 16584 * bit mask as defined by {@link #MEASURED_SIZE_MASK} and 16585 * {@link #MEASURED_STATE_TOO_SMALL}. 16586 */ 16587 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { 16588 boolean optical = isLayoutModeOptical(this); 16589 if (optical != isLayoutModeOptical(mParent)) { 16590 Insets insets = getOpticalInsets(); 16591 int opticalWidth = insets.left + insets.right; 16592 int opticalHeight = insets.top + insets.bottom; 16593 16594 measuredWidth += optical ? opticalWidth : -opticalWidth; 16595 measuredHeight += optical ? opticalHeight : -opticalHeight; 16596 } 16597 mMeasuredWidth = measuredWidth; 16598 mMeasuredHeight = measuredHeight; 16599 16600 mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET; 16601 }
果真,咱们在View.java中找到了这个方法的具体实现,经过方法说明,得知此方法必须被onMeasure()方法调用 ,来保存测量到的宽度和高度,不然的话,会在测量时引起异常.经过代码主线 ,咱们知道它将传进去的两个参数赋给本地的mMeasuredWidth和mMeasuredHeight变量,以便在View类中使用;好了,此时咱们该抽离出来,回到咱们出发的地方:app
setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
有了上面的分析过程,咱们知道这个方法中的measureWidth(widthMeasureSpec)是做为测量到的宽度,measureHeight(heightMeasureSpec)是做为测量到的高度,而这两个是须要咱们在自定义的View中去实现的,因为测量宽度与高度的过程相似,咱们在此文中仅分析measureWidth()的过程,很天然地,咱们看看本地的measureWidth()是如何实现的:less
1 /** 2 * Determines the width of this view 3 * 4 * @param measureSpec 5 * A measureSpec packed into an int 6 * @return The width of the view, honoring constraints from measureSpec 7 */ 8 private int measureWidth(int measureSpec) { 9 int result = 0; 10 int specMode = MeasureSpec.getMode(measureSpec); 11 int specSize = MeasureSpec.getSize(measureSpec); 12 13 if (specMode == MeasureSpec.EXACTLY) { 14 // We were told how big to be 15 result = specSize; 16 } else { 17 // Measure the text 18 result = (int) mTextPaint.measureText(mText) + getPaddingLeft() 19 + getPaddingRight(); 20 if (specMode == MeasureSpec.AT_MOST) { 21 // Respect AT_MOST value if that was what is called for by 22 // measureSpec 23 result = Math.min(result, specSize); 24 } 25 } 26 27 return result; 28 }
该方法用来肯定咱们自定义的这个View的宽度,它接收onMeasure()的widthMeasureSpec参数,接着ide
int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec);
MeasureSpec.getMode(measureSpec),getMode()?咱们在上一篇文章中的最后,有以下描述:
MeasureSpec:
该对象封装了父容器传递给子元素的布局要求,它有三种模式:
1)
UNSPECIFIED:父容器对子元素没有要求,子元素能够获得任意值;
2)
EXACTLY:父窗口决定子元素的大小,子元素将被限定在给定的边界里而忽略它自己大小;
3)
AT MOST:子元素至多达到父窗口指定的大小,子元素不能超过这个边界;
因此咱们会想,getMode()方法,应该就是获取上述这三种模式之一吧?咱们跟进源码,看看getMode()都作了哪些事情:oop
18341 /** 18342 * Extracts the mode from the supplied measure specification. 18343 * 18344 * @param measureSpec the measure specification to extract the mode from 18345 * @return {@link android.view.View.MeasureSpec#UNSPECIFIED}, 18346 * {@link android.view.View.MeasureSpec#AT_MOST} or 18347 * {@link android.view.View.MeasureSpec#EXACTLY} 18348 */ 18349 public static int getMode(int measureSpec) { 18350 return (measureSpec & MODE_MASK); 18351 }
由此方法的文字描述部分,咱们得知,该方法从接收的参数measureSpec中,获取到对应的三种模式之一,即返回measureSpec & MODE_MASK,这里的MODE_MASK又是个什么东西呢?在View.java中,咱们找到在View这个类中,有个内部类MeasureSpec类源码分析
18289 public static class MeasureSpec { 18290 private static final int MODE_SHIFT = 30; 18291 private static final int MODE_MASK = 0x3 << MODE_SHIFT;
..............................................................
18297 public static final int UNSPECIFIED = 0 << MODE_SHIFT;
18298
18299 /**
18300 * Measure specification mode: The parent has determined an exact size
18301 * for the child. The child is going to be given those bounds regardless
18302 * of how big it wants to be.
18303 */
18304 public static final int EXACTLY = 1 << MODE_SHIFT;
18305
18306 /**
18307 * Measure specification mode: The child can be as large as it wants up
18308 * to the specified size.
18309 */
18310 public static final int AT_MOST = 2 << MODE_SHIFT;
................................
}
因此,MODE_MASK的值为0x3左移了MODE_SHIFT(30)位,那么,用32位的二进制来表示的话,MODE_MASK为:1100 0000 0000 0000 0000 0000 0000 0000;若是非要探究此时的measureSpec & MODE_MASK后的值是多少,那么咱们不妨用Debug模式调试一下咱们的代码来获取getMode方法中传进来的参数measureSpec是什么值, 首先,从上面的源码中,能够知道三种MeasureSpec三种模式的值:
UNSPECIFIED = 0 << MODE_SHIFT;即:UNSPECIFIED为:0000 0000 0000 0000 0000 0000 0000 0000
其实咱们也能够参阅官方文档对此值的定义:
Measure specification mode: The parent has not imposed any constraint on the child. It can be whatever size it wants.
注:只不过官方文档此处用十六进制表示而已,如下两个模式也都用十六进制表示而已.
EXACTLY = 1 << MODE_SHIFT;即 EXACTLY为:0100 0000 0000 0000 0000 0000 0000 0000
Measure specification mode: The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
AT_MOST = 2 << MODE_SHIFT;即 AT_MOST为:1000 0000 0000 0000 0000 0000 0000 0000
Measure specification mode: The child can be as large as it wants up to the specified size.
MODE_MASK为:1100 0000 0000 0000 0000 0000 0000 0000
好,咱们来看一下debug前,自定义的View在布局文件中的layout_width的配置及我所调试的设备的屏幕像素为480*800,也就是个人显示屏宽为480像素;
<com.project.summary.customview.CustomView android:id="@+id/customView" android:layout_width="wrap_content" android:layout_height="wrap_content" app:colorValue="@color/textRed" app:textSize="20sp" app:textString="This the Custom View1!!!" />
调试结果出来,此时传入的measureSpec的值是-2147483648,到了这里,咱们又会产生一个疑问 ,为何是它?为何是这个值?咱们先把这个疑问作个标记:Q02;到了文章最后,这个疑问就能解开了,这里先把思路跳出来,继续分析咱们的measureWidth()这个本地方法的代码;
1 /** 2 * Determines the width of this view 3 * 4 * @param measureSpec 5 * A measureSpec packed into an int 6 * @return The width of the view, honoring constraints from measureSpec 7 */ 8 private int measureWidth(int measureSpec) { 9 int result = 0; 10 int specMode = MeasureSpec.getMode(measureSpec); 11 int specSize = MeasureSpec.getSize(measureSpec); 12 13 if (specMode == MeasureSpec.EXACTLY) { 14 // We were told how big to be 15 result = specSize; 16 } else { 17 // Measure the text 18 result = (int) mTextPaint.measureText(mText) + getPaddingLeft() 19 + getPaddingRight(); 20 if (specMode == MeasureSpec.AT_MOST) { 21 // Respect AT_MOST value if that was what is called for by 22 // measureSpec 23 result = Math.min(result, specSize); 24 } 25 } 26 27 return result; 28 }
上面咱们已经分析到第10行,因为第11行是获取传入的measureSpec的大小,过程与获取传入的measureSpec的模式相似,这里暂时先略过,接下来看第13行代码,这里要对获取到的模式进行判断,由上一篇文章,咱们知道,若是自定义的View在布局文件中指定固定大小,那么,它的模式就是属于MeasureSepc.EXACTLY,此时,measureWidth()这个本地方法就返回11行所得的大小,不然进入另一个分支,由于本系列中咱们实现的实现上是一个相似于TextView的自定义控件,那么,这个View的大小就应该由它所绘制的文字长度来决定,此时,咱们先计算出文字的宽度,而后再对其模式进行判断,若是模式是属于measureSpec.AT_MOST,咱们经过数学运算,比较文字长度与经过传入的measureSpec所包含的大小,它们之中更小的那个作为咱们控件的宽度.
文章开头的相关代码中,本地方法:getMeasureHeight()的过程与本地方法getMeasureWidth()相似,在此再也不分析.
在此总结一下,文章开头引用的代码是咱们在编写自定义View时,在重写onMeasure()这个方法时的通常步骤,那么,本文中的分析过程当中还留有两个疑问:
Q01:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)是一个override的方法,它接收两个参数,经过字面意思,咱们知道,这两个参数分别为宽度测量规格,高度测量规格,此时,咱们会有一个疑问,这两个参数是从哪里来的?
Q02:
调试结果出来,此时传入的measureSpec的值是-2147483648,到了这里,咱们又会产生一个疑问 ,为何是它?为何是这个值?
要探究这两个疑问,咱们在本系列第二篇文章中,曾经提过Android绘制View的理论基础,从那篇文章中,咱们明白,Android要绘制View的时候,必需要先遍历View的树形结构,而且先从最顶端的结点开始遍历,经过查找官方文档,咱们进入
ViewRootImpl.java(文件位于:/frameworks/base/core/java/android/view/ViewRootImpl.java),一块儿找出上面的那两个疑问.........
/*********************************友情提醒:开始下面的探究前,最好先休息一下*********************************/
咱们先大体浏览一下ViewRootImpl.java,这个文件代码有6707行有没有,不用怕,咱们先找到一个叫performtraversals()的方法,看这字面意思,它是要开始遍历的节奏啊,果断跟进去看一下,顺便找找几个有用的干货:
private void performTraversals() { ....................................... 1122 WindowManager.LayoutParams lp = mWindowAttributes;//详见分析PERFORMTRAVERSALS()点1 ......................................................... 1155 Rect frame = mWinFrame;//详见分析PERFORMTRAVERSALS()点2 ....................................................... 1563 if (mWidth != frame.width() || mHeight != frame.height()) { 1564 mWidth = frame.width(); 1565 mHeight = frame.height(); 1566 } 1567 ....................................................................... PERFORMTRAVERSALS()点3: 1634 if (!mStopped) { 1635 boolean focusChangedDueToTouchMode = ensureTouchModeLocally( 1636 (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0); 1637 if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth() 1638 || mHeight != host.getMeasuredHeight() || contentInsetsChanged) { 1639 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);//详见getRootMeasureSpec()方法的分析 1640 int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); 1641 1642 if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed! mWidth=" 1643 + mWidth + " measuredWidth=" + host.getMeasuredWidth() 1644 + " mHeight=" + mHeight 1645 + " measuredHeight=" + host.getMeasuredHeight() 1646 + " coveredInsetsChanged=" + contentInsetsChanged); 1647 1648 // Ask host how big it wants to be 1649 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); }
/************************************分析PERFORMTRAVERSALS()点1 开始**********************************/
这里的lp用得还挺多,也许对咱们有用,
由于
WindowManager.LayoutParams lp = mWindowAttributes;
因此咱们分析一下这个mWindowAttributes是何方神圣:
分析PERFORMTRAVERSALS()点1:mWindowAttributes相关代码:
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
咱们进入WindowManager类的内部类LayoutParams的构造方法
1 public LayoutParams() { 2 super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); 3 type = TYPE_APPLICATION; 4 format = PixelFormat.OPAQUE; 5 }
其中有这么一句:注意两个参数都为LayoutParams.MATCH_PARENT
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
由于WindowManager类的内部类LayoutParams继承自ViewGroup.LayoutParams,因此进入ViewGroup的内部类LayoutParams看一下
/frameworks/base/core/java/android/view/ViewGroup.java:
5829 public static class LayoutParams { 5830 /** 5831 * Special value for the height or width requested by a View. 5832 * FILL_PARENT means that the view wants to be as big as its parent, 5833 * minus the parent's padding, if any. This value is deprecated 5834 * starting in API Level 8 and replaced by {@link #MATCH_PARENT}. 5835 */ 5836 @SuppressWarnings({"UnusedDeclaration"}) 5837 @Deprecated 5838 public static final int FILL_PARENT = -1; .......................................... 5918 public LayoutParams(int width, int height) { 5919 this.width = width; 5920 this.height = height; 5921 }
分析总结:这里的width与height,都被赋为LayoutParams.MATCH_PARENT,因此这里的lp的宽与高,都为LayoutParams.MATCH_PARENT
/************************************分析PERFORMTRAVERSALS()点1 结束**********************************/
######################################################################################################################
/************************************分析PERFORMTRAVERSALS()点2 开始**********************************/
1563 if (mWidth != frame.width() || mHeight != frame.height()) { 1564 mWidth = frame.width(); 1565 mHeight = frame.height(); 1566 })
此时的mWidth为ViewRootImpl的变量,在这里使它的值为frame.width()的值;
frame又是从哪里来的呢?在performTraversals()方法中,1155行,原来它只是个局部变量,
1155 Rect frame = mWinFrame;
到了这里,关键就是找出mWinFrame了,继续找mWinFrame:
在ViewRootImpl的变量声明中:
256 final Rect mWinFrame; // frame given by window manager.
在ViewRootImpl这个类的构造方法中:
360 mWinFrame = new Rect();
frame given by window manager?那大概就是说mWinFrame是由窗口管理类来赋值的了,那么这么里mWinFrame应该就是屏幕的窗口大小了.咱们这里先这么假设,后续文章再进行验证.
/**********************************************分析PERFORMTRAVERSALS()点2 结束**************************************/
/**********************************************分析PERFORMTRAVERSALS()点3开始**************************************/
PERFORMTRAVERSALS()点3:performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
1,两个参数:childWidthMeasureSpec,childHeightMeasureSpec分析
a)childWidthMeasureSpec:
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
先分析getRootMeasureSpec的两个参数:
1)mWidth:
见分析点2(
1563 if (mWidth != frame.width() || mHeight != frame.height()) {
1564 mWidth = frame.width();
1565 mHeight = frame.height();
1566 })
因此猜测mWidth就是窗口的初始宽度(本文暂未验证)
2)lp.width:这里的lp就是分析点1中的 WindowManager.LayoutParams lp = mWindowAttributes;即:lp.width为LayoutParams.MATCH_PARENT;
由以上1)和2),咱们先搞定了getRootMeasureSpec(mWidth,lp.width)这个方法的两个参数的意义,接下来,咱们进入getRootMeasureSpec(mWidth,lp.width)这个方法
b)childHeightMeasureSpec:
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
先分析getRootMeasureSpec的两个参数:
1)mHeight:相似上述的猜测,这里的mHeight就是窗口的初始高度
2)lp.height:这里的lp就是分析点1中的 WindowManager.LayoutParams lp = mWindowAttributes;即:lp.height为LayoutParams.MATCH_PARENT;
由于上述a)与b)的调用过程相似,只不过a)是获取宽度的规格,b)是获取高度的规格,因此如下分析只以获取宽度规格的过程来分析
*******************************************************************进入getRootMeasureSpec()方法的分析**********************************************
1924 private static int getRootMeasureSpec(int windowSize, int rootDimension) { 1925 int measureSpec; 1926 switch (rootDimension) { 1927 1928 case ViewGroup.LayoutParams.MATCH_PARENT: 1929 // Window can't resize. Force root view to be windowSize. 1930 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); 1931 break; 1932 case ViewGroup.LayoutParams.WRAP_CONTENT: 1933 // Window can resize. Set max size for root view. 1934 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); 1935 break; 1936 default: 1937 // Window wants to be an exact size. Force root view to be that size. 1938 measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); 1939 break; 1940 } 1941 return measureSpec; 1942 }
此方法接收的第二个参数rootDimension,就是lp.width,经过上面的分析,lp.width=LayoutParams.MATCH_PARENT,因此,进入第一个switch分支
此方法的返回值measureSpec=MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
因此,分析此方法,咱们也知道,当咱们的自定义View的layout_width/layout_height设置成MATCH_PARENT时,MODE 为MeasureSpec.EXACTLY;当设置成WRAP_CONTENT时,MODE为MeasureSpec.AT_MOST;
接下来咱们分析1938行:
1938 measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
***********************************************************************************************************************************************************
*******************************************************************进入MeasureSpec.makeMeasureSpec()方法的分析**********************************************
17245 /** 17246 * Creates a measure specification based on the supplied size and mode. 17247 * 17248 * The mode must always be one of the following: 17249 * <ul> 17250 * <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li> 17251 * <li>{@link android.view.View.MeasureSpec#EXACTLY}</li> 17252 * <li>{@link android.view.View.MeasureSpec#AT_MOST}</li> 17253 * </ul> 17254 * 17255 * @param size the size of the measure specification 17256 * @param mode the mode of the measure specification 17257 * @return the measure specification based on size and mode 17258 */ 17259 public static int makeMeasureSpec(int size, int mode) { 17260 return size + mode; 17261 }
此方法在/frameworks/base/core/java/android/view/View.java中的内部类MeasureSpec中的方法,该方法返回两个参数size+mode之和,参数size对应咱们传进来的windowSize,即:窗口的初始宽度(当传进来的是mHeight时,为窗口的初始高度);
参数mode对应咱们传进来的MeasureSpec.EXACTLY
**************************************************************************************************************************************************************************
有了上面这些分析以后,咱们能够进入performMeasure(childWidthMeasureSpec, childHeightMeasureSpec)的分析了:
1913 private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) { 1914 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure"); 1915 try { 1916 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); 1917 } finally { 1918 Trace.traceEnd(Trace.TRACE_TAG_VIEW); 1919 } 1920 }
*************************************mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)的分析************************************************************
16450 /** 16451 * <p> 16452 * This is called to find out how big a view should be. The parent 16453 * supplies constraint information in the width and height parameters. 16454 * </p> 16455 * 16456 * <p> 16457 * The actual measurement work of a view is performed in 16458 * {@link #onMeasure(int, int)}, called by this method. Therefore, only 16459 * {@link #onMeasure(int, int)} can and must be overridden by subclasses. 16460 * </p> 16461 * 16462 * 16463 * @param widthMeasureSpec Horizontal space requirements as imposed by the 16464 * parent 16465 * @param heightMeasureSpec Vertical space requirements as imposed by the 16466 * parent 16467 * 16468 * @see #onMeasure(int, int) 16469 */ 16470 public final void measure(int widthMeasureSpec, int heightMeasureSpec) { ..................................................... 16496 // measure ourselves, this should set the measured dimension flag back 16497 onMeasure(widthMeasureSpec, heightMeasureSpec); 16498 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT; 16522 }
这里的measure()方法是个final方法,结合该方法的说明,
The actual measurement work of a view is performed in onMeasure()
而且measure的两个参数同时传入onMeasure()中,
因此,才有了文章开头时引用的代码,在自定义的View中,重写onMeasure()方法,那么,本文上部分遗留下来的两个问题,至此就有了答案:
Q01:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)是一个override的方法,它接收两个参数,经过字面意思,咱们知道,这两个参数分别为宽度测量规格,高度测量规格,此时,咱们会有一个疑问,这两个参数是从哪里来的?
经过:1639 int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);//详见getRootMeasureSpec()方法的分析,onMeasure的第一个参数widthMeasureSpec就是这里的childWidthMeasureSpec,heightMeasureSpec对应 childHeightMeasureSpec;
Q02:
调试结果出来,此时传入的measureSpec的值是-2147483648,到了这里,咱们又会产生一个疑问 ,为何是它?为何是这个值?
那么这里的measureSpec就是MeasureSpec.makeMeasureSpec()方法的分析中,返回的size+mode;size是手机显示屏的像素宽或者高,文章上半部分中,我调试的手机像素宽是480,并且在自定义的View的布局文件中,layout_width设置成wrap_content,经过上面的分析,当设置成wrap_content时,模式为AT_MOST模式,经过文档描述,它的十进制值是-2147483648,那么size+mode就是480+(-2147483648)=-2147483168,也就是咱们调试出来时,所获得的值-2147483648
)
另外,咱们或许还会有一个疑问 :为何MODE_MASK是1100 0000 0000 0000 0000 0000 0000 0000?EXACTLY为:0100 0000 0000 0000 0000 0000 0000 0000?
AT_MOST为:1000 0000 0000 0000 0000 0000 0000 0000?
其实对于这个问题,咱们想,既然android规定了MODE必须是EXACTLY,AT_MOST,UNSPECIFIED这三种模式之一,那么,就能够用32位二进制的最高两位来表示,它有00,01,10,11这四种状况,那么它的MODE_MASK取值为
1100 0000 0000 0000 0000 0000 0000 0000就能很方便地取到它的模式了,由getMode()的实现:
return (measureSpec & MODE_MASK);
咱们就能够取到它的最高两位,由此来肯定它是哪一种模式;同理对于getSize():
public static int getSize(int measureSpec) { return (measureSpec & ~MODE_MASK); }
对于屏幕宽度,再大的屏幕也用不了32位二进制来表示其尺寸,因此才有measureSpec & ~MODE_MASK,这样就能取到它的值了.
转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gamil.com