您的位置:首页 > 移动开发 > Android开发

Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程

2014-02-14 16:57 911 查看
转载请注明出处
/article/7153953.html
Fromcrash_coderlinguowu
linguowu0622@gamil.com


前言:

  通过Android自定义View及其在布局文件中的使用示例和Android自定义View及其在布局文件中的使用示例(二),我们知道了如何使用自定义的View,以及Android绘制View的理论基础,其包含三个过程,测量View大小(通过onMeasure()方法实现),计算View位置(通过onLayout()方法实现),最后开始绘制(通过onDraw()方法实现),本篇,我们将结合Android4.4.2_r1源码详细分析测量过程的具体实现.

  在第一篇里,我们提供了一个自定义的View的源代码,现在引用一下该代码与测量相关的部分:

@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
}

/**
*Determinesthewidthofthisview
*
*@parammeasureSpec
*AmeasureSpecpackedintoanint
*@returnThewidthoftheview,honoringconstraintsfrommeasureSpec
*/
privateintmeasureWidth(intmeasureSpec){
intresult=0;
intspecMode=MeasureSpec.getMode(measureSpec);
intspecSize=MeasureSpec.getSize(measureSpec);

if(specMode==MeasureSpec.EXACTLY){
//Weweretoldhowbigtobe
result=specSize;
}else{
//Measurethetext
result=(int)mTextPaint.measureText(mText)+getPaddingLeft()
+getPaddingRight();
if(specMode==MeasureSpec.AT_MOST){
//RespectAT_MOSTvalueifthatwaswhatiscalledforby
//measureSpec
result=Math.min(result,specSize);
}
}

returnresult;
}

/**
*Determinestheheightofthisview
*
*@parammeasureSpec
*AmeasureSpecpackedintoanint
*@returnTheheightoftheview,honoringconstraintsfrommeasureSpec
*/
privateintmeasureHeight(intmeasureSpec){
intresult=0;
intspecMode=MeasureSpec.getMode(measureSpec);
intspecSize=MeasureSpec.getSize(measureSpec);

mAscent=(int)mTextPaint.ascent();
if(specMode==MeasureSpec.EXACTLY){
//Weweretoldhowbigtobe
result=specSize;
}else{
//Measurethetext(beware:ascentisanegativenumber)
result=(int)(-mAscent+mTextPaint.descent())+getPaddingTop()
+getPaddingBottom();
if(specMode==MeasureSpec.AT_MOST){
//RespectAT_MOSTvalueifthatwaswhatiscalledforby
//measureSpec
result=Math.min(result,specSize);
}
}
returnresult;
}


我们可以看到:protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)是一个override的方法,它接收两个参数,通过字面意思,我们知道,这两个参数分别为宽度测量规格,高度测量规格,此时,我们会有一个疑问,这两个参数是从哪里来的?这个疑问咱们先记下来,给它编个号:Q01,暂时略过,到本文下一部分,我们就知道它的来龙去脉了.接着,我们来看onMeasure方法在本地的实现:


setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));


我们跟进setMeasuredDimension(int,int)方法,看看它到底都做了些什么事情:

因为我们自定义的View是继承自View,所以我们进入View.java(源码位置:/frameworks/base/core/java/android/view/View.java)去看看有没有这个方法:

16575/**
16576*<p>Thismethodmustbecalledby{@link#onMeasure(int,int)}tostorethe
16577*measuredwidthandmeasuredheight.Failingtodosowilltriggeran
16578*exceptionatmeasurementtime.</p>
16579*
16580*@parammeasuredWidthThemeasuredwidthofthisview.Maybeacomplex
16581*bitmaskasdefinedby{@link#MEASURED_SIZE_MASK}and
16582*{@link#MEASURED_STATE_TOO_SMALL}.
16583*@parammeasuredHeightThemeasuredheightofthisview.Maybeacomplex
16584*bitmaskasdefinedby{@link#MEASURED_SIZE_MASK}and
16585*{@link#MEASURED_STATE_TOO_SMALL}.
16586*/
16587protectedfinalvoidsetMeasuredDimension(intmeasuredWidth,intmeasuredHeight){
16588booleanoptical=isLayoutModeOptical(this);
16589if(optical!=isLayoutModeOptical(mParent)){
16590Insetsinsets=getOpticalInsets();
16591intopticalWidth=insets.left+insets.right;
16592intopticalHeight=insets.top+insets.bottom;
16593
16594measuredWidth+=optical?opticalWidth:-opticalWidth;
16595measuredHeight+=optical?opticalHeight:-opticalHeight;
16596}
16597mMeasuredWidth=measuredWidth;
16598mMeasuredHeight=measuredHeight;
16599
16600mPrivateFlags|=PFLAG_MEASURED_DIMENSION_SET;
16601}


果然,我们在View.java中找到了这个方法的具体实现,通过方法说明,得知此方法必须被onMeasure()方法调用,来保存测量到的宽度和高度,否则的话,会在测量时引发异常.通过代码主线,我们知道它将传进去的两个参数赋给本地的mMeasuredWidth和mMeasuredHeight变量,以便在View类中使用;好了,此时我们该抽离出来,回到我们出发的地方:

setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));


有了上面的分析过程,我们知道这个方法中的measureWidth(widthMeasureSpec)是作为测量到的宽度,measureHeight(heightMeasureSpec)是作为测量到的高度,而这两个是需要我们在自定义的View中去实现的,由于测量宽度与高度的过程类似,我们在此文中仅分析measureWidth()的过程,很自然地,我们看看本地的measureWidth()是如何实现的:

/**
*Determinesthewidthofthisview
*
*@parammeasureSpec
*AmeasureSpecpackedintoanint
*@returnThewidthoftheview,honoringconstraintsfrommeasureSpec
*/
privateintmeasureWidth(intmeasureSpec){
intresult=0;
intspecMode=MeasureSpec.getMode(measureSpec);
intspecSize=MeasureSpec.getSize(measureSpec);

if(specMode==MeasureSpec.EXACTLY){
//Weweretoldhowbigtobe
result=specSize;
}else{
//Measurethetext
result=(int)mTextPaint.measureText(mText)+getPaddingLeft()
+getPaddingRight();
if(specMode==MeasureSpec.AT_MOST){
//RespectAT_MOSTvalueifthatwaswhatiscalledforby
//measureSpec
result=Math.min(result,specSize);
}
}

returnresult;
}


该方法用来确定我们自定义的这个View的宽度,它接收onMeasure()的widthMeasureSpec参数,接着

intspecMode=MeasureSpec.getMode(measureSpec);
intspecSize=MeasureSpec.getSize(measureSpec);


MeasureSpec.getMode(measureSpec),getMode()?我们在上一篇文章中的最后,有如下描述:


MeasureSpec:

  该对象封装了父容器传递给子元素的布局要求,它有三种模式:

1)
UNSPECIFIED:父容器对子元素没有要求,子元素可以得到任意值;
2)
EXACTLY:父窗口决定子元素的大小,子元素将被限定在给定的边界里而忽略它本身大小;
3)
ATMOST:子元素至多达到父窗口指定的大小,子元素不能超过这个边界;


所以我们会想,getMode()方法,应该就是获取上述这三种模式之一吧?我们跟进源码,看看getMode()都做了哪些事情:

18341/**
18342*Extractsthemodefromthesuppliedmeasurespecification.
18343*
18344*@parammeasureSpecthemeasurespecificationtoextractthemodefrom
18345*@return{@linkandroid.view.View.MeasureSpec#UNSPECIFIED},
18346*{@linkandroid.view.View.MeasureSpec#AT_MOST}or
18347*{@linkandroid.view.View.MeasureSpec#EXACTLY}
18348*/
18349publicstaticintgetMode(intmeasureSpec){
18350return(measureSpec&MODE_MASK);
18351}


由此方法的文字描述部分,我们得知,该方法从接收的参数measureSpec中,获取到对应的三种模式之一,即返回measureSpec&MODE_MASK,这里的MODE_MASK又是个什么东西呢?在View.java中,我们找到在View这个类中,有个内部类MeasureSpec类

18289publicstaticclassMeasureSpec{
18290privatestaticfinalintMODE_SHIFT=30;
18291privatestaticfinalintMODE_MASK=0x3<<MODE_SHIFT;
        ..............................................................

 18297publicstaticfinalintUNSPECIFIED=0<<MODE_SHIFT;
 18298
18299/**
18300*Measurespecificationmode:Theparenthasdeterminedanexactsize
18301*forthechild.Thechildisgoingtobegiventhoseboundsregardless
18302*ofhowbigitwantstobe.
18303*/
18304publicstaticfinalintEXACTLY=1<<MODE_SHIFT;
18305
18306/**
18307*Measurespecificationmode:Thechildcanbeaslargeasitwantsup
18308*tothespecifiedsize.
18309*/
18310publicstaticfinalintAT_MOST=2<<MODE_SHIFT;

       ................................
      }


所以,MODE_MASK的值为0x3左移了MODE_SHIFT(30)位,那么,用32位的二进制来表示的话,MODE_MASK为:11000000000000000000000000000000;如果非要探究此时的measureSpec&MODE_MASK后的值是多少,那么我们不妨用Debug模式调试一下我们的代码来获取getMode方法中传进来的参数measureSpec是什么值,首先,从上面的源码中,可以知道三种MeasureSpec三种模式的值:

UNSPECIFIED=0<<MODE_SHIFT;即:UNSPECIFIED为:00000000000000000000000000000000

其实我们也可以参阅官方文档对此值的定义:

publicstaticfinalintUNSPECIFIED

AddedinAPIlevel1

Measurespecificationmode:Theparenthasnotimposedanyconstraintonthechild.Itcanbewhateversizeitwants.

ConstantValue:0(0x00000000)


注:只不过官方文档此处用十六进制表示而已,以下两个模式也都用十六进制表示而已.



EXACTLY=1<<MODE_SHIFT;即EXACTLY为:01000000000000000000000000000000


publicstaticfinalintEXACTLY


AddedinAPIlevel1


Measurespecificationmode:Theparenthasdeterminedanexactsizeforthechild.Thechildisgoingtobegiventhoseboundsregardlessofhowbigitwantstobe.

ConstantValue:1073741824(0x40000000)



AT_MOST=2<<MODE_SHIFT;即AT_MOST为:10000000000000000000000000000000


publicstaticfinalintAT_MOST




AddedinAPIlevel1




Measurespecificationmode:Thechildcanbeaslargeasitwantsuptothespecifiedsize.

ConstantValue:-2147483648(0x80000000)



MODE_MASK为:11000000000000000000000000000000


好,我们来看一下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="ThistheCustomView1!!!"/>


调试结果出来,此时传入的measureSpec的值是-2147483648,到了这里,我们又会产生一个疑问,为什么是它?为什么是这个值?我们先把这个疑问做个标记:Q02;到了文章最后,这个疑问就能解开了,这里先把思路跳出来,继续分析我们的measureWidth()这个本地方法的代码;

1/**
2*Determinesthewidthofthisview
3*
4*@parammeasureSpec
5*AmeasureSpecpackedintoanint
6*@returnThewidthoftheview,honoringconstraintsfrommeasureSpec
7*/
8privateintmeasureWidth(intmeasureSpec){
9intresult=0;
10intspecMode=MeasureSpec.getMode(measureSpec);
11intspecSize=MeasureSpec.getSize(measureSpec);
12
13if(specMode==MeasureSpec.EXACTLY){
14//Weweretoldhowbigtobe
15result=specSize;
16}else{
17//Measurethetext
18result=(int)mTextPaint.measureText(mText)+getPaddingLeft()
19+getPaddingRight();
20if(specMode==MeasureSpec.AT_MOST){
21//RespectAT_MOSTvalueifthatwaswhatiscalledforby
22//measureSpec
23result=Math.min(result,specSize);
24}
25}
26
27returnresult;
28}


上面我们已经分析到第10行,由于第11行是获取传入的measureSpec的大小,过程与获取传入的measureSpec的模式类似,这里暂时先略过,接下来看第13行代码,这里要对获取到的模式进行判断,由上一篇文章,我们知道,如果自定义的View在布局文件中指定固定大小,那么,它的模式就是属于MeasureSepc.EXACTLY,此时,measureWidth()这个本地方法就返回11行所得的大小,否则进入另外一个分支,因为本系列中我们实现的实现上是一个类似于TextView的自定义控件,那么,这个View的大小就应该由它所绘制的文字长度来决定,此时,我们先计算出文字的宽度,然后再对其模式进行判断,如果模式是属于measureSpec.AT_MOST,我们通过数学运算,比较文字长度与通过传入的measureSpec所包含的大小,它们之中更小的那个做为我们控件的宽度.

  文章开头的相关代码中,本地方法:getMeasureHeight()的过程与本地方法getMeasureWidth()类似,在此不再分析.

  在此总结一下,文章开头引用的代码是我们在编写自定义View时,在重写onMeasure()这个方法时的一般步骤,那么,本文中的分析过程中还留有两个疑问:

Q01:
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)是一个override的方法,它接收两个参数,通过字面意思,我们知道,这两个参数分别为宽度测量规格,高度测量规格,此时,我们会有一个疑问,这两个参数是从哪里来的?


Q02:
调试结果出来,此时传入的measureSpec的值是-2147483648,到了这里,我们又会产生一个疑问,为什么是它?为什么是这个值?


要探究这两个疑问,我们在本系列第二篇文章中,曾经提过Android绘制View的理论基础,从那篇文章中,我们明白,Android要绘制View的时候,必须要先遍历View的树形结构,并且先从最顶端的结点开始遍历,通过查找官方文档,我们进入

ViewRootImpl.java(文件位于:/frameworks/base/core/java/android/view/ViewRootImpl.java),一起找出上面的那两个疑问.........

/*********************************友情提醒:开始下面的探究前,最好先休息一下*********************************/

我们先大致浏览一下ViewRootImpl.java,这个文件代码有6707行有没有,不用怕,我们先找到一个叫performtraversals()的方法,看这字面意思,它是要开始遍历的节奏啊,果断跟进去看一下,顺便找找几个有用的干货:

privatevoidperformTraversals()
{
.......................................
1122WindowManager.LayoutParamslp=mWindowAttributes;//详见分析PERFORMTRAVERSALS()点1

.........................................................
1155Rectframe=mWinFrame;//详见分析PERFORMTRAVERSALS()点2
.......................................................
1563if(mWidth!=frame.width()||mHeight!=frame.height()){
1564mWidth=frame.width();
1565mHeight=frame.height();
1566}
1567
.......................................................................
PERFORMTRAVERSALS()点3:
1634if(!mStopped){
1635booleanfocusChangedDueToTouchMode=ensureTouchModeLocally(
1636(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE)!=0);
1637if(focusChangedDueToTouchMode||mWidth!=host.getMeasuredWidth()
1638||mHeight!=host.getMeasuredHeight()||contentInsetsChanged){
1639intchildWidthMeasureSpec=getRootMeasureSpec(mWidth,lp.width);//详见getRootMeasureSpec()方法的分析
1640intchildHeightMeasureSpec=getRootMeasureSpec(mHeight,lp.height);
1641
1642if(DEBUG_LAYOUT)Log.v(TAG,"Ooops,somethingchanged!mWidth="
1643+mWidth+"measuredWidth="+host.getMeasuredWidth()
1644+"mHeight="+mHeight
1645+"measuredHeight="+host.getMeasuredHeight()
1646+"coveredInsetsChanged="+contentInsetsChanged);
1647
1648//Askhosthowbigitwantstobe
1649performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
}


/************************************分析PERFORMTRAVERSALS()点1开始**********************************/

这里的lp用得还挺多,也许对我们有用,

因为

WindowManager.LayoutParamslp=mWindowAttributes;


所以我们分析一下这个mWindowAttributes是何方神圣:
分析PERFORMTRAVERSALS()点1:mWindowAttributes相关代码:

finalWindowManager.LayoutParamsmWindowAttributes=newWindowManager.LayoutParams();


我们进入WindowManager类的内部类LayoutParams的构造方法

publicLayoutParams(){
super(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
type=TYPE_APPLICATION;
format=PixelFormat.OPAQUE;
}


其中有这么一句:注意两个参数都为LayoutParams.MATCH_PARENT

super(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);


因为WindowManager类的内部类LayoutParams继承自ViewGroup.LayoutParams,所以进入ViewGroup的内部类LayoutParams看一下
/frameworks/base/core/java/android/view/ViewGroup.java:

5829publicstaticclassLayoutParams{
5830/**
5831*SpecialvaluefortheheightorwidthrequestedbyaView.
5832*FILL_PARENTmeansthattheviewwantstobeasbigasitsparent,
5833*minustheparent'spadding,ifany.Thisvalueisdeprecated
5834*startinginAPILevel8andreplacedby{@link#MATCH_PARENT}.
5835*/
5836@SuppressWarnings({"UnusedDeclaration"})
5837@Deprecated
5838publicstaticfinalintFILL_PARENT=-1;

..........................................

5918publicLayoutParams(intwidth,intheight){
5919this.width=width;
5920this.height=height;
5921}


分析总结:这里的width与height,都被赋为LayoutParams.MATCH_PARENT,所以这里的lp的宽与高,都为LayoutParams.MATCH_PARENT

/************************************分析PERFORMTRAVERSALS()点1结束**********************************/

######################################################################################################################

/************************************分析PERFORMTRAVERSALS()点2开始**********************************/

1563if(mWidth!=frame.width()||mHeight!=frame.height()){
1564mWidth=frame.width();
1565mHeight=frame.height();
1566})


此时的mWidth为ViewRootImpl的变量,在这里使它的值为frame.width()的值;
frame又是从哪里来的呢?在performTraversals()方法中,1155行,原来它只是个局部变量,

1155Rectframe=mWinFrame;


到了这里,关键就是找出mWinFrame了,继续找mWinFrame:
在ViewRootImpl的变量声明中:

256finalRectmWinFrame;//framegivenbywindowmanager.


在ViewRootImpl这个类的构造方法中:

360mWinFrame=newRect();


framegivenbywindowmanager?那大概就是说mWinFrame是由窗口管理类来赋值的了,那么这么里mWinFrame应该就是屏幕的窗口大小了.我们这里先这么假设,后续文章再进行验证.

/**********************************************分析PERFORMTRAVERSALS()点2结束**************************************/

/**********************************************分析PERFORMTRAVERSALS()点3开始**************************************/
PERFORMTRAVERSALS()点3:performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
1,两个参数:childWidthMeasureSpec,childHeightMeasureSpec分析
a)childWidthMeasureSpec:
intchildWidthMeasureSpec=getRootMeasureSpec(mWidth,lp.width);
先分析getRootMeasureSpec的两个参数:
1)mWidth:
见分析点2(
1563if(mWidth!=frame.width()||mHeight!=frame.height()){
1564mWidth=frame.width();
1565mHeight=frame.height();
1566})
所以猜想mWidth就是窗口的初始宽度(本文暂未验证)
2)lp.width:这里的lp就是分析点1中的WindowManager.LayoutParamslp=mWindowAttributes;即:lp.width为LayoutParams.MATCH_PARENT;
由以上1)和2),我们先搞定了getRootMeasureSpec(mWidth,lp.width)这个方法的两个参数的意义,接下来,我们进入getRootMeasureSpec(mWidth,lp.width)这个方法

b)childHeightMeasureSpec:
intchildHeightMeasureSpec=getRootMeasureSpec(mHeight,lp.height);
先分析getRootMeasureSpec的两个参数:
1)mHeight:类似上述的猜想,这里的mHeight就是窗口的初始高度
2)lp.height:这里的lp就是分析点1中的WindowManager.LayoutParamslp=mWindowAttributes;即:lp.height为LayoutParams.MATCH_PARENT;

因为上述a)与b)的调用过程类似,只不过a)是获取宽度的规格,b)是获取高度的规格,所以以下分析只以获取宽度规格的过程来分析
*******************************************************************进入getRootMeasureSpec()方法的分析**********************************************

1924privatestaticintgetRootMeasureSpec(intwindowSize,introotDimension){
1925intmeasureSpec;
1926switch(rootDimension){
1927
1928caseViewGroup.LayoutParams.MATCH_PARENT:
1929//Windowcan'tresize.ForcerootviewtobewindowSize.
1930measureSpec=MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.EXACTLY);
1931break;
1932caseViewGroup.LayoutParams.WRAP_CONTENT:
1933//Windowcanresize.Setmaxsizeforrootview.
1934measureSpec=MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.AT_MOST);
1935break;
1936default:
1937//Windowwantstobeanexactsize.Forcerootviewtobethatsize.
1938measureSpec=MeasureSpec.makeMeasureSpec(rootDimension,MeasureSpec.EXACTLY);
1939break;
1940}
1941returnmeasureSpec;
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行:

1938measureSpec=MeasureSpec.makeMeasureSpec(rootDimension,MeasureSpec.EXACTLY);


***********************************************************************************************************************************************************

*******************************************************************进入MeasureSpec.makeMeasureSpec()方法的分析**********************************************

17245/**
17246*Createsameasurespecificationbasedonthesuppliedsizeandmode.
17247*
17248*Themodemustalwaysbeoneofthefollowing:
17249*<ul>
17250*<li>{@linkandroid.view.View.MeasureSpec#UNSPECIFIED}</li>
17251*<li>{@linkandroid.view.View.MeasureSpec#EXACTLY}</li>
17252*<li>{@linkandroid.view.View.MeasureSpec#AT_MOST}</li>
17253*</ul>
17254*
17255*@paramsizethesizeofthemeasurespecification
17256*@parammodethemodeofthemeasurespecification
17257*@returnthemeasurespecificationbasedonsizeandmode
17258*/
17259publicstaticintmakeMeasureSpec(intsize,intmode){
17260returnsize+mode;
17261}


此方法在/frameworks/base/core/java/android/view/View.java中的内部类MeasureSpec中的方法,该方法返回两个参数size+mode之和,参数size对应我们传进来的windowSize,即:窗口的初始宽度(当传进来的是mHeight时,为窗口的初始高度);
参数mode对应我们传进来的MeasureSpec.EXACTLY

**************************************************************************************************************************************************************************
有了上面这些分析之后,我们可以进入performMeasure(childWidthMeasureSpec,childHeightMeasureSpec)的分析了:

1913privatevoidperformMeasure(intchildWidthMeasureSpec,intchildHeightMeasureSpec){
1914Trace.traceBegin(Trace.TRACE_TAG_VIEW,"measure");
1915try{
1916mView.measure(childWidthMeasureSpec,childHeightMeasureSpec);
1917}finally{
1918Trace.traceEnd(Trace.TRACE_TAG_VIEW);
1919}
1920}


*************************************mView.measure(childWidthMeasureSpec,childHeightMeasureSpec)的分析************************************************************

16450/**
16451*<p>
16452*Thisiscalledtofindouthowbigaviewshouldbe.Theparent
16453*suppliesconstraintinformationinthewidthandheightparameters.
16454*</p>
16455*
16456*<p>
16457*Theactualmeasurementworkofaviewisperformedin
16458*{@link#onMeasure(int,int)},calledbythismethod.Therefore,only
16459*{@link#onMeasure(int,int)}canandmustbeoverriddenbysubclasses.
16460*</p>
16461*
16462*
16463*@paramwidthMeasureSpecHorizontalspacerequirementsasimposedbythe
16464*parent
16465*@paramheightMeasureSpecVerticalspacerequirementsasimposedbythe
16466*parent
16467*
16468*@see#onMeasure(int,int)
16469*/
16470publicfinalvoidmeasure(intwidthMeasureSpec,intheightMeasureSpec){
         .....................................................
16496//measureourselves,thisshouldsetthemeasureddimensionflagback
16497onMeasure(widthMeasureSpec,heightMeasureSpec);
16498mPrivateFlags3&=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

16522}


这里的measure()方法是个final方法,结合该方法的说明,

TheactualmeasurementworkofaviewisperformedinonMeasure()


并且measure的两个参数同时传入onMeasure()中,

所以,才有了文章开头时引用的代码,在自定义的View中,重写onMeasure()方法,那么,本文上部分遗留下来的两个问题,至此就有了答案:

Q01:
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)是一个override的方法,它接收两个参数,通过字面意思,我们知道,这两个参数分别为宽度测量规格,高度测量规格,此时,我们会有一个疑问,这两个参数是从哪里来的?


通过:1639intchildWidthMeasureSpec=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是11000000000000000000000000000000?EXACTLY为:01000000000000000000000000000000?

AT_MOST为:10000000000000000000000000000000?

其实对于这个问题,我们想,既然android规定了MODE必须是EXACTLY,AT_MOST,UNSPECIFIED这三种模式之一,那么,就可以用32位二进制的最高两位来表示,它有00,01,10,11这四种情况,那么它的MODE_MASK取值为

11000000000000000000000000000000就能很方便地取到它的模式了,由getMode()的实现:

return(measureSpec&MODE_MASK);


我们就可以取到它的最高两位,由此来确定它是哪种模式;同理对于getSize():

publicstaticintgetSize(intmeasureSpec){
return(measureSpec&~MODE_MASK);
}


对于屏幕宽度,再大的屏幕也用不了32位二进制来表示其尺寸,所以才有measureSpec&~MODE_MASK,这样就能取到它的值了.

转载请注明出处
/article/7153953.html
Fromcrash_coderlinguowu
linguowu0622@gamil.com




                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐