Android自定义View和Canvas绘图解析.docx

上传人:b****5 文档编号:7308526 上传时间:2023-01-22 格式:DOCX 页数:47 大小:33.79KB
下载 相关 举报
Android自定义View和Canvas绘图解析.docx_第1页
第1页 / 共47页
Android自定义View和Canvas绘图解析.docx_第2页
第2页 / 共47页
Android自定义View和Canvas绘图解析.docx_第3页
第3页 / 共47页
Android自定义View和Canvas绘图解析.docx_第4页
第4页 / 共47页
Android自定义View和Canvas绘图解析.docx_第5页
第5页 / 共47页
点击查看更多>>
下载资源
资源描述

Android自定义View和Canvas绘图解析.docx

《Android自定义View和Canvas绘图解析.docx》由会员分享,可在线阅读,更多相关《Android自定义View和Canvas绘图解析.docx(47页珍藏版)》请在冰豆网上搜索。

Android自定义View和Canvas绘图解析.docx

Android自定义View和Canvas绘图解析

Android自定义View和Canvas绘图解析

 

自定义view的流程分为measure、layout、draw三个主要步骤,今天我们通过源码来分下下measure的过程

我们从顶级view开始,顶级view即DecorView,view的事件都是先经过这个DecorView,接下来我们来看看这个DecorView的MeasureSpec的创建过程:

 

ViewRoot对应ViewRootImpl类,是连接WindowManager和DecorView的纽带,进入ViewRootImpl中,查看measureHierarchy方法,有如下代码:

finalDisplayMetricspackageMetrics=res.getDisplayMetrics();

res.getValue(com.android.internal.R.dimen.config_prefDialogWidth,mTmpValue,true);

intbaseSize=0;

if(mTmpValue.type==TypedValue.TYPE_DIMENSION){

baseSize=(int)mTmpValue.getDimension(packageMetrics);

}

if(DEBUG_DIALOG)Log.v(mTag,"Window"+mView+":

baseSize="+baseSize

+",desiredWindowWidth="+desiredWindowWidth);

if(baseSize!

=0&&desiredWindowWidth>baseSize){

childWidthMeasureSpec=getRootMeasureSpec(baseSize,lp.width);

childHeightMeasureSpec=getRootMeasureSpec(desiredWindowHeight,lp.height);

performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);

这里只是截选一部分的源码,我们看到这个baseSize,其实就是屏幕的尺寸大小,获取宽的MeasureSpc的方法:

childWidthMeasureSpec=getRootMeasureSpec(baseSize,lp.width);

这里传入的参数是屏幕尺寸以及DecorView自身的大小,接着我们来看getRootMeasureSpec方法:

 

privatestaticintgetRootMeasureSpec(intwindowSize,introotDimension){

intmeasureSpec;

switch(rootDimension){

caseViewGroup.LayoutParams.MATCH_PARENT:

//Windowcan'tresize.ForcerootviewtobewindowSize.

measureSpec=MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.EXACTLY);

break;

caseViewGroup.LayoutParams.WRAP_CONTENT:

//Windowcanresize.Setmaxsizeforrootview.

measureSpec=MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.AT_MOST);

break;

default:

//Windowwantstobeanexactsize.Forcerootviewtobethatsize.

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

break;

}

returnmeasureSpec;

}

就是这个方法确定了DecorView的MeasureSpec,这里分了三种情况,

1.如果传入的view大小为math_parent,那么这个view的mode为EXACTLY,大小为屏幕的尺寸.

2.如果传入的view大小为wrap_content,那么这个view的mode为AT_MOST,大小为屏幕的尺寸.

3.如果传入的view大小为一个具体的值,那么这个view的mode为EXACTLY,大小为view本身大小。

 

以上就是DecorView的MeaureSpec的整个创建的过程了。

 

看了顶级view之后我们来看普通的view,普通的view的measure过程是由viewgroup传递过来的,接着我们来看看viewgroup的measureChildWithMargins方法:

protectedvoidmeasureChildWithMargins(Viewchild,

intparentWidthMeasureSpec,intwidthUsed,

intparentHeightMeasureSpec,intheightUsed){

finalMarginLayoutParamslp=(MarginLayoutParams)child.getLayoutParams();

finalintchildWidthMeasureSpec=getChildMeasureSpec(parentWidthMeasureSpec,

mPaddingLeft+mPaddingRight+lp.leftMargin+lp.rightMargin

+widthUsed,lp.width);

finalintchildHeightMeasureSpec=getChildMeasureSpec(parentHeightMeasureSpec,

mPaddingTop+mPaddingBottom+lp.topMargin+lp.bottomMargin

+heightUsed,lp.height);

child.measure(childWidthMeasureSpec,childHeightMeasureSpec);

}

这个方法获得了子view的MeasureSpec,并且将其传入子view的measure方法中,这里重点来看下viewgroup是如何创建子view的MeasuerSpec的。

来看getChildMeasureSpec方法内部的实现:

publicstaticintgetChildMeasureSpec(intspec,intpadding,intchildDimension){

intspecMode=MeasureSpec.getMode(spec);

intspecSize=MeasureSpec.getSize(spec);

intsize=Math.max(0,specSize-padding);

intresultSize=0;

intresultMode=0;

switch(specMode){

//Parenthasimposedanexactsizeonus

caseMeasureSpec.EXACTLY:

if(childDimension>=0){

resultSize=childDimension;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.MATCH_PARENT){

//Childwantstobeoursize.Sobeit.

resultSize=size;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.WRAP_CONTENT){

//Childwantstodetermineitsownsize.Itcan'tbe

//biggerthanus.

resultSize=size;

resultMode=MeasureSpec.AT_MOST;

}

break;

//Parenthasimposedamaximumsizeonus

caseMeasureSpec.AT_MOST:

if(childDimension>=0){

//Childwantsaspecificsize...sobeit

resultSize=childDimension;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.MATCH_PARENT){

//Childwantstobeoursize,butoursizeisnotfixed.

//Constrainchildtonotbebiggerthanus.

resultSize=size;

resultMode=MeasureSpec.AT_MOST;

}elseif(childDimension==LayoutParams.WRAP_CONTENT){

//Childwantstodetermineitsownsize.Itcan'tbe

//biggerthanus.

resultSize=size;

resultMode=MeasureSpec.AT_MOST;

}

break;

//Parentaskedtoseehowbigwewanttobe

caseMeasureSpec.UNSPECIFIED:

if(childDimension>=0){

//Childwantsaspecificsize...lethimhaveit

resultSize=childDimension;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.MATCH_PARENT){

//Childwantstobeoursize...findouthowbigitshould

//be

resultSize=View.sUseZeroUnspecifiedMeasureSpec?

0:

size;

resultMode=MeasureSpec.UNSPECIFIED;

}elseif(childDimension==LayoutParams.WRAP_CONTENT){

//Childwantstodetermineitsownsize....findouthow

//bigitshouldbe

resultSize=View.sUseZeroUnspecifiedMeasureSpec?

0:

size;

resultMode=MeasureSpec.UNSPECIFIED;

}

break;

}

//noinspectionResourceType

returnMeasureSpec.makeMeasureSpec(resultSize,resultMode);

}

这个方法很长,但是我们只需要注意到AT_MOST跟EXACTLY这两种情况就行,稍微分析下这个过程:

首先要理解这个方法的三个参数,第一个是父view的MeasureSpec,第二个是父view已占用的大小,第三个是view的LayoutParams的大小,如果不理解可以看看ViewGroup的MeasureChildWithMargins方法中的调用:

 

finalintchildWidthMeasureSpec=getChildMeasureSpec(parentWidthMeasureSpec,

mPaddingLeft+mPaddingRight+lp.leftMargin+lp.rightMargin

+widthUsed,lp.width);

第二个参数很长,mPaddingLeft+mPaddingRight+lp.leftMargin+lp.rightMargin+withUsed,这些所有的值都有一个共同特点,就是这些位置是不能摆放任何view的,即父view已经占用的地盘,现在是不是对参数更加理解了呢。

 

接着我们回到getChildMeasureSpec方法中继续看看viewGroup到底是怎么创建view的MeasureSpec的。

 

第一步:

根据参数一,即传入的父view的MeasureSpec获得父view的Mode和Size。

这里的第三行代码:

intsize=Math.max(0,specSize-padding);

这个size表示取0与父容器中可占用的位置的最大值,可以直接理解为父view的大小。

第二步:

根据父view的Mode分情况处理,到这一步我们应该就清楚为什么说view的大小是由父view的MeasureSpec与本身LayoutParmas大小共同决定的吧。

 

这里我们依然只看AT_MOST跟EXACTLY两种情况,

switch(specMode){

//Parenthasimposedanexactsizeonus

caseMeasureSpec.EXACTLY:

if(childDimension>=0){

resultSize=childDimension;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.MATCH_PARENT){

//Childwantstobeoursize.Sobeit.

resultSize=size;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.WRAP_CONTENT){

//Childwantstodetermineitsownsize.Itcan'tbe

//biggerthanus.

resultSize=size;

resultMode=MeasureSpec.AT_MOST;

}

break;

//Parenthasimposedamaximumsizeonus

caseMeasureSpec.AT_MOST:

if(childDimension>=0){

//Childwantsaspecificsize...sobeit

resultSize=childDimension;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.MATCH_PARENT){

//Childwantstobeoursize,butoursizeisnotfixed.

//Constrainchildtonotbebiggerthanus.

resultSize=size;

resultMode=MeasureSpec.AT_MOST;

}elseif(childDimension==LayoutParams.WRAP_CONTENT){

//Childwantstodetermineitsownsize.Itcan'tbe

//biggerthanus.

resultSize=size;

resultMode=MeasureSpec.AT_MOST;

}

break;

这里有个比较难以理解的值就是childDimension>0,这个其实就表示view的大小是一个具体的值比如100dp,因为view的match_parent和wrap_content在系统内部定义的都是负数,一个是-1.一个是-2,所以判断childDimension>0即,view的大小为一个具体的值。

接着就比较好理解了,我们来稍微总结下:

无论父view是match_parent还是wrap_content,只要view是一个具体的值,view的Mode永远都是EXACTLY,大小均是view本身定义的大小。

父view模式如果是EXACTLY,--->子view如果是mathch_parent,那么子view的大小是父view的大小,模式也跟父view一样为EXACTLY.子view如果是wrap_content,大小还是父view的大小,模式为AT_MOST

 

父view模式如果是AT_MOST,--->子view如果是math_parent,那么子view大小为父view大小,模式与父view一样都是AT_MOST,子view如果是wrap_content,子view大小为父view大小,模式为AT_MOST

 

上面说的有点绕,但其实我们只需要记住一点,无论上面那种情况,子view在wrap_content下,大小都是父view的大小,到这里我们是不是就能理解为什么在自定义view的过程中如果不重写onMeasure,wrap_content是和match_parent是一个效果了吧。

 

以上过程是viewGroup中创建子view的MeasureSpec的过程,有了这个MeasureSpec,测量子view大小就很简单了,我们可以看到在ViewGroup获取到子view的MeasureSpec之后,传入到子view的measure方法中:

 

child.measure(childWidthMeasureSpec,childHeightMeasureSpec);

进入到view的measure方法,

不知不觉我们已经从viewgroup进入到了view的测量过程,

 

这里是不是突然意识到,ViewGroup根本没有测绘自己本身啊,只是获取到子view的MeasureSpec然后传入子view的measure方法里去,这是因为ViewGroup是个抽象类,本身并没有定义测量的过程,ViewGroup的onMeasure需要各个子类去实现,比如LinearLayout、RelativeLayout等等,并且每个子类的测量过程都不一样,这个我们后面会讲,现在我们还是接着看view的Measure过程。

 

上面说到viewgroup将创建的子view的MeasureSpec传入到了view的Measure方法中,那么我们就来看看View的Measure方法:

publicfinalvoidmeasure(intwidthMeasureSpec,intheightMeasureSpec){

booleanoptical=isLayoutModeOptical(this);

if(optical!

=isLayoutModeOptical(mParent)){

Insetsinsets=getOpticalInsets();

intoWidth=insets.left+insets.right;

intoHeight=insets.top+insets.bottom;

widthMeasureSpec=MeasureSpec.adjust(widthMeasureSpec,optical?

-oWidth:

oWidth);

heightMeasureSpec=MeasureSpec.adjust(heightMeasureSpec,optical?

-oHeight:

oHeight);

}

//Suppresssignextensionforthelowbytes

longkey=(long)widthMeasureSpec<<32|(long)heightMeasureSpec&0xffffffffL;

if(mMeasureCache==null)mMeasureCache=newLongSparseLongArray

(2);

finalbooleanforceLayout=(mPrivateFlags&PFLAG_FORCE_LAYOUT)==PFLAG_FORCE_LAYOUT;

//OptimizelayoutbyavoidinganextraEXACTLYpasswhentheviewis

//alreadymeasuredasthecorrectsize.InAPI23andbelow,this

//extrapassisrequiredtomakeLinearLayoutre-distributeweight.

finalbooleanspecChanged=widthMeasureSpec!

=mOldWidthMeasureSpec

||heightMeasureSpec!

=mOldHeightMeasureSpec;

finalbooleanisSpecExactly=MeasureSpec.getMode(widthMeasureSpec)==MeasureSpec.EXACTLY

&&MeasureSpec.getMode(heightMeasureSpec)==MeasureSpec.EXACTLY;

finalbooleanmatchesSpecSize=getMeasuredWidth()==MeasureSpec.getSize(widthMeasureSpec)

&&getMeasuredHeight()==Measur

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 理学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1