View的工作原理(一)之 View的三大过程 和 认识MeasureSpec
来源:互联网 发布:r语言数据挖掘 薛薇 编辑:程序博客网 时间:2024/04/30 10:07
View的三大过程
一个View出现在屏幕要经过三个过程:measure(测量)、layout(布局)、draw(绘制)。这三个过程均是通过ViewRoot来完成,ViewRoot对应ViewRootImpl类,它是连接WindowManager和DecorView(顶级View)的纽带。当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。
measure过程决定了View的宽/高,Measure完成以后,可以通过getMeasuredWidth和getMeasuredHeight方法来获取到View测量后的宽/高,在几乎所有情况下它都等于View最终的宽/高,但特殊情况除外。
layout过程决定了View的四个顶点的坐标和实际的View的宽/高,完成以后,可通过getTop、getBottom、getLeft和getRight来拿到View的四个顶点的位置,并可通过getWidth和getHeight方法来拿到View的最终宽/高。
draw过程决定了View的显示。
DecorView就是顶级View或叫根View,一般情况下是一个竖直方向的LinearLayout,分Title和Content两部分(具体看Android版本及主题),我们在Activity的onCreate方法中调用的setContentView就是设置它的Content内容。Content这块的id是content,所以,如果想获得它的对象,可以这样:
ViewGroup contentView = (ViewGroup)findViewById(android.R.id.content);
然而我们通过setContentView设置的View,可以这样获得:contentView.getChildAt(0);。它们几个的关系,可以参考下图:
ViewRoot的工作
ViewRootImpl类中有方法performTraversals,performTraversals会依次调用performMeasure、performLayout和performDraw三个方法,它们分别完成DecorView的measure、layout 和 draw三大流程。从源码可以看到:perormMeasure方法 调用了 measure方法,measure 方法又调用onMeasure 方法。在onMeasure方法中则会对所有子元素进行measure过程,这时measure流程就从父容器传递到子元素中,这样就完成一次measure过程。接着子元素会重复父容器的过程。同理performLayout和performDraw类似。可以参考下图:
MeasureSpec
一般情况下,我们在做界面布局时会LayoutParams有三种参数,分别是:match_parent、wrap_content 和 数值大小(例如:10dp)。其实这三种参数对应了两种模式,这种模式叫SpecMode(测量模式)。要了解SpecMode是什么就要先来说说MeasureSpec。MeasureSpec代表一个32位int值,高2位代表SpecMode,低30位代表SpecSize(测量模式下的规格大小)。MeasureSpec至所以通过SpecMode和SpecSize打包成一个int值,想必是为了避免过多的对象内存分配。
View的尺寸规格是受父容器影响和MeasureSpec的决定。在measure过程中,系统会将View的Layoutparams根据父容器所施加的规则转换成对应的MeasureSpec值,此值就是用于测量出View的宽和高。注意测量的宽和高不一定等于最终宽和高。
说回刚才的三种参数对应两种SpecMode,其实SpecMode共有三类,每一类表示的含义如下所示:
UNSPECIFIED 父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
EXACTLY 父容器已检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应LayoutParams中的match_parent和具体的数值这两种模式。
AT_MOST 父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的wrap_content
DecorView获得MeasureSpec过程
DecorView(顶级View)其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定。可见在ViewRootImpl中的measureHierarchy方法中有如下一段代码展示了DecorView的MeasureSpec的创建过程,其中desiredWindowWidth和desiredWindowHeight是屏幕的尺寸:
private boolean measureHierarchy(View host,LayoutParams lp, Resources res, int desiredWindowWidth, intdesiredWindowHeight) { …… childWidthMeasureSpec= getRootMeasureSpec (desiredWindowWidth, lp.width); childHeightMeasureSpec= getRootMeasureSpec (desiredWindowHeight, lp.height); performMeasure(childWidthMeasureSpec,childHeightMeasureSpec); ……}
getRootMeasureSpec方法的实现:
private static int getRootMeasureSpec(int windowSize,int rootDimension) { intmeasureSpec; switch(rootDimension) { caseViewGroup.LayoutParams.MATCH_PARENT: // 精确模式,大小就是窗口的大小。 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; caseViewGroup.LayoutParams.WRAP_CONTENT: // 最大模式,大小不定,但是不能超过窗口的大小。 measureSpec = MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.AT_MOST); break; default: // 精确模式,大小为LayoutParams中指定的大小。 measureSpec = MeasureSpec.makeMeasureSpec(rootDimension,MeasureSpec.EXACTLY); break; } returnmeasureSpec;}
View获得MeasureSpec过程
View的measure过程由ViewGroup传递而来,先看一下ViewGroup的measureChildWithMargins(在实现类的onMeasure中调用,如:LinearLayout->measureVertical-> measureChildBeforeLayout-> measureChildWithMargins)方法:
protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, intheightUsed) { finalMarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final intchildWidthMeasureSpec = getChildMeasureSpec (parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed,lp.width); final intchildHeightMeasureSpec = getChildMeasureSpec (parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed,lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}
在调用子元素的measure方法之前会先通过getChildMeasureSpec方法来得到子元素的MeaureSpec。所以说,子元素的MeasureSpec的创建与父容器的MeasureSpec和子元素本身的LayoutParams有关,此外还和View的margin及padding有关。我们来看看getChildMeasureSpec方法的实现:
public static int getChildMeasureSpec(int spec, intpadding, int childDimension) { int specMode= MeasureSpec.getMode(spec); intspecSize = MeasureSpec.getSize(spec); int size =Math.max(0, specSize - padding); intresultSize = 0; intresultMode = 0; switch(specMode) { // 父元素是精确模式,大小就是窗口的大小(MATCH_PARENT或指定大小)。 case MeasureSpec.EXACTLY: if(childDimension >= 0) { //子元素是它自己设定的数值大小 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // 子元素要成为跟父元素一样的尺寸 resultSize = size; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // 子元素要根据内容认定自己的大小,但不能比父元素大 resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // 父元素是最大模式,大小不定,但是不能超过窗口的大小(WRAP_CONTENT)。 caseMeasureSpec.AT_MOST: if(childDimension >= 0) { // 子元素是它自己设定的数值大小 resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { // 子元素要成为跟父元素一样的尺寸,但父元素不是固定的,而且子元素不会比父元素大 resultSize = size; resultMode = MeasureSpec.AT_MOST; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { // 子元素要根据内容认定自己的大小,但不能比父元素大 resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parentasked to see how big we want to be(这模式主要用于系统内部多次Measure情形,一般来说,我们不需要关注此模式) caseMeasureSpec.UNSPECIFIED: if(childDimension >= 0) { //Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } elseif (childDimension == LayoutParams.MATCH_PARENT) { //Child wants to be our size... find out how big it should //be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } elseif (childDimension == LayoutParams.WRAP_CONTENT) { //Child wants to determine its own size.... find out how //big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } returnMeasureSpec.makeMeasureSpec(resultSize, resultMode);}
——本博文部分内容参考自《Android开发艺术探索》
- View的工作原理(一)之 View的三大过程 和 认识MeasureSpec
- View的工作流程-对MeasureSpec认识
- View的工作原理之MeasureSpec测量规格
- View的工作原理 理解MeasureSpec
- View的工作原理(三)--View的Layout和Draw过程
- View的工作原理之measure过程
- View的工作原理之layout过程
- Android的View工作原理(一)mearsure过程
- Framework之View的工作原理(一)
- View的工作原理(二)View的三大流程
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- View的工作原理(三)
- View工作原理(四)view的layout过程
- [置顶] View工作原理(四)view的layout过程
- android-View工作原理(四)view的layout过程
- View工作原理(四)view的layout过程
- android-View工作原理(四)view的layout过程
- Matlab2014的下载和安装过程
- loadrunner Web_类函数之web_reg_save_param_ex()
- Java静态绑定与动态绑定
- Centos启动——修改启动时背景图片
- 【拜小白opencv】34-图像平滑处理,6种滤波总结的综合示例【盒式滤波、均值滤波、高斯滤波、中值滤波、双边滤波、导向滤波】
- View的工作原理(一)之 View的三大过程 和 认识MeasureSpec
- 照着写了一个在线购物网站产品展示的页面
- 刚网上测评了算法工程师的笔试,才发现自己的编程有多烂!
- JDBC技术对数据库的操作
- Delphi中ClientDataSet浅析
- Error: java.util.concurrent.ExecutionException: com.android.ide.common.process.ProcessException
- redis安装和主从配置
- 扩展欧几里得算法(exgcd)
- java 数据库操作JDBC