View绘制流程.docx

上传人:b****5 文档编号:3978029 上传时间:2022-11-26 格式:DOCX 页数:15 大小:316.28KB
下载 相关 举报
View绘制流程.docx_第1页
第1页 / 共15页
View绘制流程.docx_第2页
第2页 / 共15页
View绘制流程.docx_第3页
第3页 / 共15页
View绘制流程.docx_第4页
第4页 / 共15页
View绘制流程.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

View绘制流程.docx

《View绘制流程.docx》由会员分享,可在线阅读,更多相关《View绘制流程.docx(15页珍藏版)》请在冰豆网上搜索。

View绘制流程.docx

View绘制流程

View绘制流程第一步:

递归measure源码分析

//final方法,子类不可重写

publicfinalvoidmeasure(intwidthMeasureSpec,int

heightMeasureSpec){

......

//回调onMeasure()方法

onMeasure(widthMeasureSpec,heightMeasureSpec);

}

这个方法的两个参数都是父View传递过来的,代表了父view的规格。

他由两部分组成,高2位表示MODE,低30位表示size。

//View的onMeasure默认实现方法

protectedvoidonMeasure(intwidthMeasureSpec,

intheightMeasureSpec){

setMeasuredDimension(

getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),

getDefaultSize(getSuggestedMinimumHeight(),heightMeasureSpec)

);

}

对于非ViewGroup的View而言,通过调用上面默认的onMeasure即可完成View的测量。

setMeasuredDimension函数是一个很关键的函数,它完成了对View的成员变量mMeasuredWidth和mMeasuredHeight变量赋值。

publicstaticintgetDefaultSize(intsize,intmeasureSpec){

intresult=size;

//通过MeasureSpec解析获取mode与size

intspecMode=MeasureSpec.getMode(measureSpec);

intspecSize=MeasureSpec.getSize(measureSpec);

switch(specMode){

caseMeasureSpec.UNSPECIFIED:

result=size;

break;

caseMeasureSpec.AT_MOST:

caseMeasureSpec.EXACTLY:

result=specSize;

break;

}

returnresult;

}

protectedintgetSuggestedMinimumWidth(){

return(mBackground==null)?

mMinWidth:

max(mMinWidth,

mBackground.getMinimumWidth());

}

protectedintgetSuggestedMinimumHeight(){

return(mBackground==null)?

mMinHeight:

max(mMinHeight,

mBackground.getMinimumHeight());

}

在ViewGroup中定义了measureChildren,measureChild,measureChildWith-

Margins方法来对子视图进行测量,measureChildren内部实质只是循环调用measureChild。

protectedvoidmeasureChildWithMargins(Viewchild,

intparentWidthMeasureSpec,intwidthUsed,

intparentHeightMeasureSpec,intheightUsed){

//获取子视图的LayoutParams

finalMarginLayoutParamslp=(MarginLayoutParams)

child.getLayoutParams();

//调整MeasureSpec

//通过这两个参数以及子视图本身LayoutParams来共同决定子视图的测量规格

finalintchildWidthMeasureSpec=

getChildMeasureSpec(parentWidthMeasureSpec,

mPaddingLeft+mPaddingRight+lp.leftMargin+lp.rightMargin

+widthUsed,lp.width);

finalintchildHeightMeasureSpec=

getChildMeasureSpec(parentHeightMeasureSpec,

mPaddingTop+mPaddingBottom+lp.topMargin+lp.bottomMargin

+heightUsed,lp.height);

//调运子View的measure方法,子View的measure中会回调子View的//onMeasure方法

child.measure(childWidthMeasureSpec,childHeightMeasureSpec);

}

该方法就是对父视图提供的measureSpec参数结合自身的LayoutParams参数进行了调整,然后再来调用child.measure()方法,具体通过方法getChildMeasureSpec来进行参数调整。

publicstaticintgetChildMeasureSpec(intspec,intpadding,int

childDimension){

//获取当前ParentView的Mode和Size

intspecMode=MeasureSpec.getMode(spec);

intspecSize=MeasureSpec.getSize(spec);

//获取Parentsize与padding差值(也就是Parent剩余大小),若差值小于0直接返回0

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

//定义返回值存储变量

intresultSize=0;

intresultMode=0;

//依据当前Parent的Mode进行switch分支逻辑

switch(specMode){

//Parenthasimposedanexactsizeonus

//默认RootView的Mode就是EXACTLY

caseMeasureSpec.EXACTLY:

if(childDimension>=0){

//如果child的layout_wOrh属性在xml或者java中给予具体大

//于等于0的数值

//设置child的size为真实layout_wOrh属性值,mode为EXACTLY

resultSize=childDimension;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.MATCH_PARENT){

//如果child的layout_wOrh属性在xml或者java中给予

//MATCH_PARENT

//Childwantstobeoursize.Sobeit.

//设置child的size为size,mode为EXACTLY

resultSize=size;

resultMode=MeasureSpec.EXACTLY;

}elseif(childDimension==LayoutParams.WRAP_CONTENT){

//如果child的layout_wOrh属性在xml或者java中给予

//WRAP_CONTENT

//设置child的size为size,mode为AT_MOST

//Childwantstodetermineitsownsize.Itcan'tbe

//biggerthanus.

resultSize=size;

resultMode=MeasureSpec.AT_MOST;

}

break;

......

//其他Mode分支类似

}

//将mode与size通过MeasureSpec方法整合为32位整数返回

returnMeasureSpec.makeMeasureSpec(resultSize,resultMode);

}

·用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。

·MeasureSpec(View的内部类)测量规格为int型,值由高2位规格模式specMode和低30位具体尺寸specSize组成。

其中specMode只有三种值:

MeasureSpec.EXACTLY//确定模式,父View希望子View的大小是确定的,由specSize决定;

MeasureSpec.AT_MOST//最多模式,父View希望子View的大小最多是specSize指定的值;

MeasureSpec.UNSPECIFIED//未指定模式,父View完全依据子View的设计值来决定;

View绘制流程第二步:

递归layout源码分析

ViewGroup的layout方法,如下:

@Override

publicfinalvoidlayout(intl,intt,intr,intb){

......

super.layout(l,t,r,b);

.....

}

调运了View父类的layout方法,所以我们看下View的layout源码,如下:

publicvoidlayout(intl,intt,intr,intb){

//实质都是调用setFrame方法把参数分别赋值给mLeft、mTop、mRight和//mBottom这几个变量

//判断View的位置是否发生过变化,以确定有没有必要对当前的View进行重新//layout

booleanchanged=isLayoutModeOptical(mParent)?

setOpticalFrame(l,t,r,b):

setFrame(l,t,r,b);

if(changed||(mPrivateFlags&PFLAG_LAYOUT_REQUIRED)==

PFLAG_LAYOUT_REQUIRED){

onLayout(changed,l,t,r,b);

}

}

//ViewGroup的onLayout方法,如下:

@Override

protectedabstractvoidonLayout(booleanchanged,

intl,intt,intr,intb);

关于getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHeight()这两对方法之间的区别

publicfinalintgetMeasuredWidth(){

returnmMeasuredWidth&MEASURED_SIZE_MASK;

}

publicfinalintgetMeasuredHeight(){

returnmMeasuredHeight&MEASURED_SIZE_MASK;

}

publicfinalintgetWidth(){

returnmRight-mLeft;

}

publicfinalintgetHeight(){

returnmBottom-mTop;

}

·View.layout方法可被重载,ViewGroup.layout为final的不可重载,ViewGroup.onLayout为abstract的,子类必须重载实现自己的位置逻辑。

·凡是layout_XXX的布局属性基本都针对的是包含子View的ViewGroup的,当对一个没有父容器的View设置相关layout_XXX属性是没有任何意义的

·使用View的getWidth()和getHeight()方法来获取View测量的宽高,必须保证这两个方法在onLayout流程之后被调用才能返回有效值

View绘制流程第三步:

递归draw源码分析

ViewGroup没有重写View的draw方法,所以如下直接从View的draw方法开始

publicvoiddraw(Canvascanvas){

//Step1,drawthebackground,ifneeded

if(!

dirtyOpaque){

drawBackground(canvas);

}

//skipstep2&5ifpossible(commoncase)

//Step2,savethecanvas'layers

if(drawTop){

canvas.saveLayer(left,top,right,top+length,null,flags);

}

//Step3,drawthecontent

if(!

dirtyOpaque)onDraw(canvas);

//Step4,drawthechildren

dispatchDraw(canvas);

//Step5,drawthefadeeffectandrestorelayers

if(drawTop){

matrix.setScale(1,fadeHeight*topFadeStrength);

matrix.postTranslate(left,top);

fade.setLocalMatrix(matrix);

p.setShader(fade);

canvas.drawRect(left,top,right,top+length,p);

}

//Step6,drawdecorations(scrollbars)

onDrawScrollBars(canvas);

}

privatevoiddrawBackground(Canvascanvas){

//获取xml中通过android:

background属性或者代码中

//setBackgroundColor()、setBackgroundResource()等方法进行赋值的背景

//Drawable

finalDrawablebackground=mBackground;

......

//根据layout过程确定的View位置来设置背景的绘制区域

if(mBackgroundSizeChanged){

background.setBounds(0,0,mRight-mLeft,mBottom-mTop);

mBackgroundSizeChanged=false;

rebuildOutline();

}

......

//调用Drawable的draw()方法来完成背景的绘制工作

background.draw(canvas);

......

}

//View的onDraw方法,这是一个空方法。

因为每个View的内容部分是各不相同的,//所以需要由子类去实现具体逻辑。

protectedvoidonDraw(Canvascanvas){

}

//View的dispatchDraw()方法是一个空方法,如果View包含子类需要重写他,所//以我们有必要看下ViewGroup的dispatchDraw方法源码

@Override

protectedvoiddispatchDraw(Canvascanvas){

......

finalintchildrenCount=mChildrenCount;

finalView[]children=mChildren;

for(inti=0;i

if((child.mViewFlags&VISIBILITY_MASK)==VISIBLE||

child.getAnimation()!

=null){

more|=drawChild(canvas,child,drawingTime);

}

}

//Drawanydisappearingviewsthathaveanimations

if(mDisappearingChildren!

=null){

for(inti=disappearingCount;i>=0;i--){

more|=drawChild(canvas,child,drawingTime);

}

}

}

//ViewGroup确实重写了View的dispatchDraw()方法,该方法内部会遍历每个子//View,然后调用drawChild()方法,我们可以看下ViewGroup的drawChild方法

protectedbooleandrawChild(Canvascanvas,Viewchild,

longdrawingTime){

returnchild.draw(canvas,this,drawingTime);

}

drawChild()方法调运了子View的draw()方法。

所以说ViewGroup类已经为我们重写了dispatchDraw()的功能实现,我们一般不需要重写该方法,但可以重载父类函数实现具体的功能。

·在获取画布剪切区时会自动处理掉padding,子View获取Canvas不用关注这些逻辑,只用关心如何绘制即可。

·默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()方法提供不同顺序。

View的invalidate方法源码分析

View类中的一些invalidate方法

//ThismustbecalledfromaUIthread.Tocallfromanon-UIthread,

//callpostInvalidate()

publicvoidinvalidate(Rectdirty){

finalintscrollX=mScrollX;

finalintscrollY=mScrollY;

//实质还是调运invalidateInternal方法

invalidateInternal(dirty.left-scrollX,dirty.top-scrollY,

dirty.right-scrollX,dirty.bottom-scrollY,true,false);

}

//ThismustbecalledfromaUIthread.Tocallfromanon-UIthread,

//callpostInvalidate()

publicvoidinvalidate(intl,intt,intr,intb){

finalintscrollX=mScrollX;

finalintscrollY=mScrollY;

//实质还是调运invalidateInternal方法

invalidateInternal(l-scrollX,t-scrollY,r-scrollX,b-scrollY,true,false);

}

//ThismustbecalledfromaUIthread.Tocallfromanon-UIthread,

//callpostInvalidate()

publicvoidinvalidate(){

//invalidate的实质还是调运invalidateInternal方法

invalidate(true);

}

//thisfunctioncanbecalledwithinvalidateCachesettofalseto//skipthatinvalidationstep

voidinvalidate(booleaninvalidateCache){

//实质还是调运invalidateInternal方法

invalidateInternal(0,0,mRight-mLeft,mBottom-mTop,

invalidateCache,true);

}

//所有invalidate的最终调运方法

voidinvalidateInternal(intl,intt,intr,intb,booleaninvalidateCache,booleanfullInvalidate){

......

//Propagatethedamagerectangletotheparentview.

finalAttachInfoai=mAttachInfo;

finalViewParentp=mParent;

if(p!

=null&&ai!

=null&&l

finalRectdamage=ai.mTmpInvalRect;

//设置刷新区域

damage.set(l,t,r,b);

//传递调运ParentViewGroup的invalidateChild方法

p.invalidateChild(this,damage);

}

......

}

View的invalidate(invalidateInternal)方法实质是将要刷新区域直接传递给了父ViewGroup的invalidateChild方法,在invalidate中,调用父View的invalidateChild,这是一个从当前向上级父View回溯的过程

ViewGroup的invalidateChild方法

publicfinalvoidinvalidateChild(Viewchild,finalRectdirty){

ViewParentparent=this;

finalAttachInfoattachInfo=mAttachInfo;

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

当前位置:首页 > 小学教育 > 数学

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

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