我们在编写layout的xml文件时会碰到layout_width和layout_height两个属性,对于这两个属性我们有三种选择:赋值成具体的数值,match_parent或者wrap_content,而measure过程就是用来处理match_parent或者wrap_content,假如layout中规定所有View的layout_width和layout_height必须赋值成具体的数值,那么measure其实是没有必要的,但是google在设计Android的时候考虑加入match_parent或者wrap_content肯定是有原因的,它们会使得布局更加灵活。
首先我们来看几个关键的函数和参数:
1、public final void measue(int widthMeasureSpec, int heightMeasureSpec);
2、protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
3、protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)
4、protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)
5、protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)
接着我们来看View类中measure和onMeasure函数的源码:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
}
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
由于函数原型中有final字段,那么measure根本没打算被子类继承,也就是说measure的过程是固定的,而measure中调用了onMeasure函数,因此真正有变数的是onMeasure函数,onMeasure的默认实现很简单,源码如下:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
onMeasure默认的实现仅仅调用了setMeasuredDimension,setMeasuredDimension函数是一个很关键的函数,它对View的成员变量mMeasuredWidth和mMeasuredHeight变量赋值,而measure的主要目的就是对View树中的每个View的mMeasuredWidth和mMeasuredHeight进行赋值,一旦这两个变量被赋值,则意味着该View的测量工作结束。
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= MEASURED_DIMENSION_SET;
}
对于非ViewGroup的View而言,通过调用上面默认的measure——>onMeasure,即可完成View的测量,当然你也可以重载onMeasure,并调用setMeasuredDimension来设置任意大小的布局,但一般不这么做,因为这种做法太“*”,至于为何“*”,读完本文就会明白。
对于ViewGroup的子类而言,往往会重载onMeasure函数负责其children的measure工作,重载时不要忘记调用setMeasuredDimension来设置自身的mMeasuredWidth和mMeasuredHeight。如果我们在layout的时候不需要依赖子视图的大小,那么不重载onMeasure也可以,但是必须重载onLayout来安排子视图的位置,这在下一篇博客中会介绍。
再来看下measue(int widthMeasureSpec, int heightMeasureSpec)中的两个参数, 这两个参数分别是父视图提供的测量规格,当父视图调用子视图的measure函数对子视图进行测量时,会传入这两个参数,通过这两个参数以及子视图本身的LayoutParams来共同决定子视图的测量规格,在ViewGroup的measureChildWithMargins函数中体现了这个过程,稍后会介绍。
MeasureSpec参数的值为int型,分为高32位和低16为,高32位保存的是specMode,低16位表示specSize,specMode分三种:
1、MeasureSpec.UNSPECIFIED,父视图不对子视图施加任何限制,子视图可以得到任意想要的大小;
2、MeasureSpec.EXACTLY,父视图希望子视图的大小是specSize中指定的大小;
3、MeasureSpec.AT_MOST,子视图的大小最多是specSize中的大小。
以上施加的限制只是父视图“希望”子视图的大小按MeasureSpec中描述的那样,但是子视图的具体大小取决于多方面的。
ViewGroup中定义了measureChildren, measureChild, measureChildWithMargins来对子视图进行测量,measureChildren内部只是循环调用measureChild,measureChild和measureChildWithMargins的区别就是是否把margin和padding也作为子视图的大小,我们主要分析measureChildWithMargins的执行过程:
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
总的来看该函数就是对父视图提供的measureSpec参数进行了调整(结合自身的LayoutParams参数),然后再来调用child.measure()函数,具体通过函数getChildMeasureSpec来进行参数调整,过程如下:
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
getChildMeasureSpec的总体思路就是通过其父视图提供的MeasureSpec参数得到specMode和specSize,并根据计算出来的specMode以及子视图的childDimension(layout_width和layout_height中定义的)来计算自身的measureSpec,如果其本身包含子视图,则计算出来的measureSpec将作为调用其子视图measure函数的参数,同时也作为自身调用setMeasuredDimension的参数,如果其不包含子视图则默认情况下最终会调用onMeasure的默认实现,并最终调用到setMeasuredDimension,而该函数的参数正是这里计算出来的。
1)不论父View 的specMode是EXACTLY、UNSPECIFIED、AT_MOST的哪一种,如果子view在xml文件里面把layout_width或者layout_height这是了具体
的dimen值、或者在代码里调用相应的方法为view的宽度或者高度设置了具体的值,那么此时widthSpec的mode 或者heightSpec的mode就是EXACLTY,size就等于layout_width或者
layout_height的值。此时子view的specMode值不受父View的specMode的影响。
2)当父View的specMode为EXACTLY的时候:父View强加给子View一个确切的大小,有如下两种情况
2.1)子View的layout_width或者layout_height设置为MATCH_PARENT的时候,子View的specMode为EXACTLY
2.2)子View的layout_width或者layout_height设置为 WRAP_CONTENT的时候,子View的specMode为AT_MOST
2.3)不论子view为match_parent或者wrap_content,resultSize都等于父类的size.
3)当父view的specMode为AT_MOST的时候:父View强加给一个最大的size给子view,最大的size也就是父view的size
3.1)此时不论子view的为match_parent或者wrap_content,子view的specMode都为AT_MOST
3.2)resultSize的大小被设置为父view的大小
4)当父view的specMode为UNSPECIFIED的时候:
4.1) 此时不论子view的为match_parent或者wrap_content,子view的specMode都为UNSPECIFIED
4.3)此时reusltSize = 0
5)从对UNSPECIFIED分析可以知道,在1)情况之外的情况下,要是想让子view自己决定自己的宽度或者高度的大小,MeasureSpec.makeMeasureSpec(0,UNSPECIFIED)这么调用来获取宽度和高度
的spec
6)根据方法的参数以及方法的实现可以知道,视图的大小是由父视图和子视图共同决定的,前提是子视图没有设置具体的dimen值
总结:从上面的描述看出,决定权最大的就是View的设计者,因为设计者可以通过调用setMeasuredDimension决定视图的最终大小,例如调用setMeasuredDimension(100, 100)将视图的mMeasuredWidth和mMeasuredHeight设置为100,100,那么父视图提供的大小以及程序员在xml中设置的layout_width和layout_height将完全不起作用,当然良好的设计一般会根据子视图的measureSpec来设置mMeasuredWidth和mMeasuredHeight的大小,已尊重程序员的意图。
通过以下方法可以强制计算当前view的宽高,原因不明
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
view.measure(w, h);
mChildHeight = view.getMeasuredHeight();
mChildWidth = view.getMeasuredWidth();
参考链接
获取view的高度和宽度(在onCreate方法中) - Listening_music的专栏 - 博客频道 - CSDN.NET
本文主要参考
Android中mesure过程详解 – - 那些人追过的年 - 博客园
Android中mesure过程详解的更多相关文章
-
Cordova 打包 Android release app 过程详解
Cordova 打包 Android release app 过程详解 时间 -- :: SegmentFault 原文 https://segmentfault.com/a/119000000517 ...
-
Android的init过程详解(一)
Android的init过程详解(一) Android的init过程(二):初始化语言(init.rc)解析 本文使用的软件版本 Android:4.2.2 Linux内核:3.1.10 本文及后续几 ...
-
Android中Service(服务)详解
http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...
-
Android中Intent组件详解
Intent是不同组件之间相互通讯的纽带,封装了不同组件之间通讯的条件.Intent本身是定义为一个类别(Class),一个Intent对象表达一个目的(Goal)或期望(Expectation),叙 ...
-
Android中的动画详解系列【4】——Activity之间切换动画
前面介绍了Android中的逐帧动画和补间动画,并实现了简单的自定义动画,这一篇我们来看看如何将Android中的动画运用到实际开发中的一个场景--Activity之间跳转动画. 一.定义动画资源 如 ...
-
Android的init过程详解(一)(转)
本文使用的软件版本 Android:4.2.2 Linux内核:3.1.10 本文及后续几篇文章将对Android的初始化(init)过程进行详细地.剥丝抽茧式地分析,并且在其中穿插了大量的知识,希望 ...
-
Android中shape属性详解
一.简单使用 刚开始,就先不讲一堆标签的意义及用法,先简单看看shape标签怎么用. 1.新建shape文件 首先在res/drawable文件夹下,新建一个文件,命名为:shape_radius.x ...
-
RxJava在Android中使用场景详解
RxJava 系列文章 <一,RxJava create操作符的用法和源码分析> <二,RxJava map操作符用法详解> <三,RxJava flatMap操作符用法 ...
-
Android中的Service详解
今天我们就来介绍一下Android中的四大组件中的服务Service,说到Service, 它分为本地服务和远程服务:区分这两种服务就是看客户端和服务端是否在同一个进程中,本地服务是在同一进程中的,远 ...
随机推荐
-
JFinal 项目 在tomcat下部署
原文:http://my.oschina.net/jfinal/blog/353062 首先明确一下 JFinal 项目是标准的 java web 项目,其部署方式与普通 java web 项目没有任 ...
-
Android编码规范04
private final String MESSAGE_WARN = "您输入的密码有误,请重新输入!"; private final String CLASS_ONE = &q ...
-
maven 依赖排除
在项目中遇到一个问题,项目使用spring 3.x,引用了某些包,这些包又依赖了spring2.x,造成项目无法启动.这种情况就需要用到maven的依赖排除,配置如下: 使用以下代码排除依赖xxxx引 ...
-
jQuery/CSS3实现图片层叠展开特效
这是一款基于jQuery和CSS3的图片层叠展开特效,让鼠标滑过图片时即可触发这些特效.其中有一款就像扇子展开收拢一样,看起来效果都非常不错.当然本文主要还是来分析一下用jQuery实现这一效果的方法 ...
-
openstack手动玩转
<一,preface Important Project Network> openstack or all most cloud env Network desgine is so m ...
-
overcast
关于一个利用relative的简单布局,firefox上出现一点问题,暂且不明原因 firefox的 chrome的 代码记录 <!DOCTYPE html> <html lang= ...
-
最大似然估计与期望最大化(EM)算法
一.最大似然估计与最大后验概率 1.概率与统计 概率与统计是两个不同的概念. 概率是指:模型参数已知,X未知,p(x1) ... p(xn) 都是对应的xi的概率 统计是指:模型参数未知,X已知,根据 ...
-
Loj #2324. 「清华集训 2017」小 Y 和二叉树
Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...
-
js 内置对象参考 (Array,String, Math, Data, Number)
var str = "helloWorld"; var strOne = "helloWorld"; // charAt() 返回在指定位置的字符. var a ...
-
Jenkins和gitlab集成自动构建
Jenkins安装插件 Jenkins上需要安装如下插件 Gitlab Hook Plugin,GitLab Plugin Job配置 在需要自动触发的Job中 选择Build Triggers进行如 ...