Android View的绘制流程三部曲Word文档下载推荐.docx

上传人:b****5 文档编号:19443073 上传时间:2023-01-06 格式:DOCX 页数:15 大小:21.41KB
下载 相关 举报
Android View的绘制流程三部曲Word文档下载推荐.docx_第1页
第1页 / 共15页
Android View的绘制流程三部曲Word文档下载推荐.docx_第2页
第2页 / 共15页
Android View的绘制流程三部曲Word文档下载推荐.docx_第3页
第3页 / 共15页
Android View的绘制流程三部曲Word文档下载推荐.docx_第4页
第4页 / 共15页
Android View的绘制流程三部曲Word文档下载推荐.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

Android View的绘制流程三部曲Word文档下载推荐.docx

《Android View的绘制流程三部曲Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Android View的绘制流程三部曲Word文档下载推荐.docx(15页珍藏版)》请在冰豆网上搜索。

Android View的绘制流程三部曲Word文档下载推荐.docx

系统是通过View的MeasureSpec来确定View的测量宽高的

MeasureSpec是怎么来的?

对于普通的View来说,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同确定。

对于顶级View(DecorView),其MeasureSpec由窗口的尺寸和其自身的LayoutParams共同确定。

我们回到performMeasure方法,来看看传入的参数childWidthMeasureSpec和childHeightMeasureSpec,这两个MeasureSpec是顶级View的,它们由窗口的尺寸和其自身的LayoutParams共同确定。

那它们又是怎么产生的?

在ViewRootImpl的measureHierarchy方法中,有两行代码是这样的:

childWidthMeasureSpec=getRootMeasureSpec(desiredWindowWidth,lp.width);

childHeightMeasureSpec=getRootMeasureSpec(desiredWindowHeight,lp.height);

getRootMeasureSpec方法获取到根View(DecorView)的MeasureSpec。

传入的参数desiredWindowWidth和desiredWindowHeight是屏幕的尺寸。

lp.width和lp.height都是MATCH_PARENT。

那么探探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);

default:

//Windowwantstobeanexactsize.Forcerootviewtobethatsize.

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

}

returnmeasureSpec;

}

会转到MeasureSpec的makeMeasureSpec方法,而makeMeasureSpec方法就是将SpecSize和SpecMode包装成32位的int值。

那makeMeasureSpec方法是怎么组装MeasureSpec的呢?

如下:

publicstaticintmakeMeasureSpec(@IntRange(from=0,to=(1<

<

MeasureSpec.MODE_SHIFT)-1)intsize,

@MeasureSpecModeintmode){

if(sUseBrokenMakeMeasureSpec){

returnsize+mode;

}else{

return(size&

~MODE_MASK)|(mode&

MODE_MASK);

这时,根View的MeasureSpec就诞生了。

它将参与构成子元素的MeasureSpec。

而对于普通的View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同构成。

我们知道刚才getRootMeasureSpec方法获取到的是顶级View的MeasureSpec,顶级View本身就是父容器。

那现在看看ViewGroup的measureChildWithMargins方法,这个方法是用来测量子View的。

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);

首先是调用子元素的getLayoutParams方法获取到子元素的LayoutParams,之后调用了getChildMeasureSpec方法来获取到子元素的MeasureSpec,可以看到传入了父元素的MeasureSpec。

getChildMeasureSpec方法很重要,能让我们了解子元素MeasureSpec的产生过程,如下:

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;

}elseif(childDimension==LayoutParams.WRAP_CONTENT){

//Childwantstodetermineitsownsize.Itcan'

tbe

//biggerthanus.

resultMode=MeasureSpec.AT_MOST;

//Parenthasimposedamaximumsizeonus

caseMeasureSpec.AT_MOST:

//Childwantsaspecificsize...sobeit

//Childwantstobeoursize,butoursizeisnotfixed.

//Constrainchildtonotbebiggerthanus.

//Parentaskedtoseehowbigwewanttobe

caseMeasureSpec.UNSPECIFIED:

//Childwantsaspecificsize...lethimhaveit

//Childwantstobeoursize...findouthowbigitshould

//be

resultSize=View.sUseZeroUnspecifiedMeasureSpec?

0:

size;

resultMode=MeasureSpec.UNSPECIFIED;

//Childwantstodetermineitsownsize....findouthow

//bigitshouldbe

//noinspectionResourceType

returnMeasureSpec.makeMeasureSpec(resultSize,resultMode);

经过了getChildMeasureSpec方法,子元素的MeasureSpec也诞生了。

这个方法代码虽然长长的,但逻辑并不复杂,就是根据父容器的MeasureSpec和子元素的LayoutParams来组装子元素的MeasureSpec。

所以说普通View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。

那么现在已经搞定了MeasureSpec,跟进performMeasure方法看看到底View的测量过程是怎样的。

View的测量

performMeasure方法源码如下:

privatevoidperformMeasure(intchildWidthMeasureSpec,intchildHeightMeasureSpec){

Trace.traceBegin(Trace.TRACE_TAG_VIEW,"

measure"

);

try{

mView.measure(childWidthMeasureSpec,childHeightMeasureSpec);

}finally{

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

转到了View的measure方法,如下:

publicfinalvoidmeasure(intwidthMeasureSpec,intheightMeasureSpec){

//代码省略

finalbooleanforceLayout=(mPrivateFlags&

PFLAG_FORCE_LAYOUT)==PFLAG_FORCE_LAYOUT;

if(forceLayout||needsLayout){

//firstclearsthemeasureddimensionflag

mPrivateFlags&

=~PFLAG_MEASURED_DIMENSION_SET;

resolveRtlPropertiesIfNeeded();

intcacheIndex=forceLayout?

-1:

mMeasureCache.indexOfKey(key);

if(cacheIndex<

0||sIgnoreMeasureCache){

//measureourselves,thisshouldsetthemeasureddimensionflagback

onMeasure(widthMeasureSpec,heightMeasureSpec);

mPrivateFlags3&

=~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;

}

mOldWidthMeasureSpec=widthMeasureSpec;

mOldHeightMeasureSpec=heightMeasureSpec;

mMeasureCache.put(key,((long)mMeasuredWidth)<

32|

(long)mMeasuredHeight&

0xffffffffL);

//suppresssignextension

可以看到View的measure方法是带final的,不允许子类重写。

经过一系列的处理,会转到onMeasure方法,那就跟进View的onMeasure方法探探:

protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){

setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),

getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec));

调用了setMeasuredDimension将测量的宽高设置进去,好像很简单的说。

getDefaultSize方法用于获取测量宽高,源码如下:

publicstaticintgetDefaultSize(intsize,intmeasureSpec){

intresult=size;

intspecMode=MeasureSpec.getMode(measureSpec);

intspecSize=MeasureSpec.getSize(measureSpec);

result=size;

result=specSize;

returnresult;

其实内部逻辑是很简单的,从measureSpec中取出specMode和specSize,然后就是AT_MOST和EXACTLY的情况下,都返回specSize,这个specSize就是测量的值了。

以上就是View的测量过程。

补充:

对于TextView、Button、ImageView等,它们都是重写了onMeasure方法的,可以阅读一下它们的onMeasure方法源码

ViewGroup的测量

那么接下来是ViewGroup的测量过程,ViewGroup中是没有重写onMeasure方法的,为什么ViewGroup不像View一样对其onMeasure方法做统一的实现呢?

我们可以想一下的,怎么能定义出一个符合多种ViewGroup的onMeasure方法呢?

很显然LinearLayout和RelativeLayout的onMeasure方法实现是不一样的。

所以需要由子类去实现,这也是很合理的。

ViewGroup除了完成自身的测量,还会遍历子元素,如此循环完成整棵视图树的测量过程。

在ViewGroup中定义了一个measureChildren方法去遍历子元素,如下:

protectedvoidmeasureChildren(intwidthMeasureSpec,intheightMeasureSpec){

finalintsize=mChildrenCount;

finalView[]children=mChildren;

for(inti=0;

i<

++i){

finalViewchild=children[i];

if((child.mViewFlags&

VISIBILITY_MASK)!

=GONE){

measureChild(child,widthMeasureSpec,heightMeasureSpec);

会转到measureChild方法中去测量子元素。

protectedvoidmeasureChild(Viewchild,intparentWidthMeasureSpec,

intparentHeightMeasureSpec){

finalLayoutParamslp=child.getLayoutParams();

mPaddingLeft+mPaddingRight,lp.width);

mPaddingTop+mPaddingBottom,lp.height);

就这样,ViewGroup将measure过程传递到了子元素。

如此反复完成整棵视图树的绘制。

以上就是ViewGroup的测量过程,至此,View的测量过程已经分析结束。

当measure过程完成后,就可以调用getMeasuredWidth/getMeasuredHeight方法来获取测量宽高了。

理解ViewGroup的测量,可以阅读下LinearLayout的onMeasure方法源码。

layout过程

在performLayout方法中转到layout方法来完成View布局过程。

那就来看看View的layout方法,如下:

publicvoidlayout(intl,intt,intr,intb){

if((mPrivateFlags3&

PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT)!

onMeasure(mOldWidthMeasureSpec,mOldHeightMeasureSpec);

intoldL=mLeft;

intoldT=mTop;

intoldB=mBottom;

intoldR=mRight;

booleanchanged=isLayoutModeOptical(mParent)?

setOpticalFrame(l,t,r,b):

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

当前位置:首页 > PPT模板 > 动物植物

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

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