Android艺术开发探索第四章View的工作原理上文档格式.docx
- 文档编号:21605508
- 上传时间:2023-01-31
- 格式:DOCX
- 页数:32
- 大小:62.41KB
Android艺术开发探索第四章View的工作原理上文档格式.docx
《Android艺术开发探索第四章View的工作原理上文档格式.docx》由会员分享,可在线阅读,更多相关《Android艺术开发探索第四章View的工作原理上文档格式.docx(32页珍藏版)》请在冰豆网上搜索。
MeasureSpec在很大程度上决定了一个View的尺寸规格,之所以说很大程度上是因为这个过程还收到了父容器的影像,因为父容器影像MeasureSpec的创建过程,在测量过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后再根据这个measureSpec来测量出View的宽高,MeasureSpec看起来有点复杂,其实他的实现很简单,我们来详细分解一下
1.MeasureSpec
MeasureSpec代表一个32位int值,高两位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式,而SpecSize是指在某个测量模式下的规格大小,下面先看一下,MeasureSpec内部的一些常量定义,通过这些就不难理解MeasureSpec的工作原理了
privatestaticfinalintMODE_SHIFT=30;
privatestaticfinalintMODE_MASK=0x3<
<
MODE_SHIFT;
publicstaticfinalintUNSPECIFIED=0<
publicstaticfinalintEXACTLY=1<
publicstaticfinalintAT_MOST=2<
publicstaticintmakeMeasureSpec(@IntRange(from=0,to=(1<
MeasureSpec.MODE_SHIFT)-1)intsize,
@MeasureSpecModeintmode){
if(sUseBrokenMakeMeasureSpec){
returnsize+mode;
}else{
return(size&
~MODE_MASK)|(mode&
MODE_MASK);
}
@MeasureSpecMode
publicstaticintgetMode(intmeasureSpec){
//noinspectionResourceType
return(measureSpec&
publicstaticintgetSize(intmeasureSpec){
~MODE_MASK);
MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配,为了方便操作,其提供了打包和解包的作用,SpecMode和specSize也是一个int值,一直SpecMode和specSize可以打包成一个MeasureSpec,一个MeasureSpec可以通过解包的形式来得出其原始的SpecMode和SpecSize,需要注意的是这里提到的MeasureSpec是指MeasureSpec所代表的int值,而非MeasureSpec本身。
SpecMode有三类,每一类都有特殊的含义
UNSPECIFIED
父容器不对View有任何的限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态
EXACTLY
父容器已经检测出View所需要的精度大小,这个时候View的最终大小就是SpecSize所指定的值,它对应于LayoutParams中的match_parent,和具体的数值这两种模式
AT_MOST
父容器指定了一个可用大小,即SpecSize,view的大小不能大于这个值,具体是什么值要看不同view的具体实现,它对应于LayoutParams中wrap_content
2.MeasureSpec和LayoutParams的对应关系
系统内部是通过MeasureSpec来进行View的测量,但是正常情况下我们使用View的测量,但是正常情况下我们使用View指定MeasureSpec,但是尽管如此,我们也可以给View设置layoutparams,在view测量的时候,系统会将layoutparams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec来确定view测量后的宽高,需要注意的是,MeasureSpec不是唯一由layoutparams决定的,layoutparams需要和父容器一起决定view的MeasureSpec从而进一步决定view的宽高,对于顶级view(DecorView)和普通的view来说,MeasureSpec的转换过程有些不同,对于decorview,其MeasureSpec由父容器的MeasureSpec和自身的layoutparams来决定,MeasureSpec一旦确定后,MeasureSpec就可以去为view测量了
对于DecorView来说,在ViewRootImpl中的measureHierarchy方法中有这么一段代码。
他展示了DecorViwew的MeasureSpec创建过程,其中desiredWindowWidth和desiredWindowHeight是屏幕的尺寸
childWidthMeasureSpec=getRootMeasureSpec(baseSize,lp.width);
childHeightMeasureSpec=getRootMeasureSpec(desiredWindowHeight,lp.height);
performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
接下来看下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;
通过上述代码,DecorView的MesourSpec的产生过程就很明确了,具体来说其遵守了如下格式,根据layoutparams的宽高的参数来划分
LayouParams.MATCH_PARENT:
精确模式,大小就是窗口的大小
LayouParams.WRAP_CONTENT:
最大模式,大小不定,但是不能超出屏幕的大小
固定大小(比如100dp):
精确模式,大小为LayoutParams中指定的大小
对于普通的View来说,这里是指我们布局中的View,View的measure过程由ViewGroup传递而来,先看下ViewGroup的measureChildWithMargis方法
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);
上述的方法会对子元素进行measure,在调用子元素的measure方法之前会通过getChildMeasureSpec方法得到子元素的MesureSpec,从代码上看,很显然,子元素的MesureSpec的创建和父容器的MesureSpec和子元素的LayoutParams有关,此外,还和view的margin有关,具体可以看下ViewGroup的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;
}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
returnMeasureSpec.makeMeasureSpec(resultSize,resultMode);
上述方法不难理解,他的主要作用是根据父容器的MeasureSpec同时结合view本身来layoutparams来确定子元素的MesureSpec,参数中的pading是指父容器中已占有的控件大小,因此子元素可以用的大小为父容器的尺寸减去pading,具体代码
intspecSize=MesureSpec.getSize(spec);
intsize=Math.max(0,specSize-pading);
getChildMeasureSpec清楚的展示了普通View的MeasureSpec同时结合View本身的LayoutParams来确定子元素的MeaureSpec的创建规则,更加清晰的理解getChildMeasureSpec的逻辑,这里提供一个表,表中对getChildMeasureSpec的工作原理进行了梳理,表中的parentSize是指父容器中目前可使用的大小:
这张表暂时不画,可以到书中看182页
针对这张表,这里再做一下说明。
前面已经提到,对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定,那么针对不同的父容器和Viev本身不同的LayoutParams,View就可以有多种MeasureSpec。
这里简单说一下,当View采用固定宽/高的时候,不管父容器的MeasureSpec是什么,View的MeasureSpee都是精确模式,那么View也是精准模式并且其大小是父容器的剩余空间;
如果父容器是最大模式,那么View也是最大模式并且其大小不会超过父容器的剩余空间。
当View的宽/高是wrap_content时,不管父容器的模式是精准还是最大化,View的模式总是最大化,并且大小不能超过父容器的剩余空间,可能读者会发现,在我们的分析中漏掉了UNSPECIFIED模式,那是因为这个模式主要用于系统内部多次Measure的情形,一般来说,我们不需要关注此模式。
通过这张表可以看出,只要提供父容器的MeasureSpec和子元素的LayoutParams,就可以快速地确定出子元素的MeasureSpec了,有了MeasureSpec就可以进一步确定出子元亲测量后的大小了。
需要说明的是,表中并非是什么经验总结,它只是getcchildMeasureSpec
这个方法以表格的方式呈现出来而已
3.View的工作流程
View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局和绘制,其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draww则将View绘制到屏幕上。
1.measure过程
measure过程要分情况来看,如果只是一个原始的View,那么通过measure方法就可以完成了其测量过程,如果是一个ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子元素的measure方法,各个子元素再递归去执行这个流程,下面针对这两种情况分别讨论
1.View的measure过程
View的measure过程由其measure方法来完成,measure方法是一个final类型的方法,这就意味着子类不能重写此方法,在View的measure方法中去调用View的onMesure方法,因此只需要看onMeasure的实现即可,View的onMesure方法如下所示:
@Override
protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){
setMeasuredDimension(
getDefaultSize(getSuggestedMinimumWidth(),widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(),height
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 艺术 开发 探索 第四 View 工作 原理
![提示](https://static.bdocx.com/images/bang_tan.gif)