Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程
2014-02-14 16:57
911 查看
转载请注明出处 /article/7153953.html Fromcrash_coderlinguowu linguowu0622@gamil.com
前言:
通过
在
@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
AddedinMeasurespecificationmode:Theparenthasnotimposedanyconstraintonthechild.Itcanbewhateversizeitwants.
ConstantValue:0(0x00000000)
注:只不过官方文档此处用十六进制表示而已,以下两个模式也都用十六进制表示而已.
EXACTLY=1<<MODE_SHIFT;即EXACTLY为:01000000000000000000000000000000
publicstaticfinalintEXACTLY
Addedin
Measurespecificationmode:Theparenthasdeterminedanexactsizeforthechild.Thechildisgoingtobegiventhoseboundsregardlessofhowbigitwantstobe.
ConstantValue:1073741824(0x40000000)
AT_MOST=2<<MODE_SHIFT;即AT_MOST为:10000000000000000000000000000000
publicstaticfinalintAT_MOST
Addedin
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
相关文章推荐
- Android 自定义View及其在布局文件中的使用示例(二)
- Android 自定义View及其在布局文件中的使用示例
- Android 自定义View及其在布局文件中的使用示例
- Android 自定义View及其在布局文件中的使用示例
- Android 自定义View及其在布局文件中的使用示例
- Android 自定义View及其在布局文件中的使用示例
- Android布局文件的加载过程分析:Activity.setContentView()源码分析
- Android中将xml布局文件转化为View树的过程分析(下)-- LayoutInflater源码分析
- Android布局优化之ViewStub、include、merge使用与源码分析
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android 自定义 View 之 onMeasure() 源码分析及重写
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android布局优化之ViewStub、include、merge使用与源码分析
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- 【Android开源项目分析】自定义圆形头像CircleImageView的使用和源码分析
- Android中将xml布局文件转化为View树的过程分析(上)