标签:
Android应用程序的界面是View组成的,这些View以树形结构组织,它们存在着父子关系,其中,子view位于父view里面,因此,在绘制一个Android应用程序窗口的View之前,我们首先要确定它里面的各个子view在父view里面的大小以及位置。确定各个子view在父view里面的大小以及位置的过程又称为测量过程和布局过程。测量和布局完后,就可以绘制view了.
参考上一篇文章Android应用程序窗口View的创建过程,可以知道Android应用程序窗口的根View是DecorView,而测量,布局,绘制这三个过程都是从DecorView开始的,然后递归到各个子view.
从Activity的onCreate(),到DecorView开始measure(),中间还经过了一些步骤.
下面先简单说一下中间的过程,然后重点分析measure的过程.
首先Activity的onCreate()函数里,我们创建应用程序的界面,也就是DecorView在这里创建好了.
接着Activity的onStart(),OnResume()相继被调用,我们就从OnResume()被调用之后往下看:
1.
在frameworks/base/core/java/android/app/ActivityThread.java中
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
..
ActivityClientRecord r = performResumeActivity(token, clearHide);//
....
wm.addView(decor, l);
}WindowManager是用来管理应用程序窗口的.
2.
在frameworks/base/core/java/android/view/WindowManagerImpl.java中
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
3.
在frameworks/base/core/java/android/view/WindowManagerGlobal.java中
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
root = new ViewRootImpl(view.getContext(), display);
..
root.setView(view, wparams, panelParentView);
...
}
4.
在frameworks/base/core/java/android/view/ViewRootImpl.java中
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
....
}mWindowSession.addToDisplay()就是把界面显示出来.
上面过程涉及了几个重要的类ViewRootImpl,WindowManagerImpl,WindowManagerGlobal,这里没详细说了,这里先了解大致流程.以后有时间再细说.
好了,现在就开始进入正题,进入requestLayout()里面.
requestLayout()里面会执行到performTraversals()
performTraversals()里面又会执行到 performMeasure(childWidthMeasureSpec,
childHeightMeasureSpec),而这个方法,就是开始测量了(Ask host how big it wants to be)
第一步:performTraversals()
在frameworks/base/core/java/android/view/ViewRootImpl.java中
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);childWidthMeasureSpec和childHeightMeasureSpec.分别表示宽度和高度的测量标准,这两个参数是传给DecorView测量用的.看获取该值的函数名字也很明显:getRootMeasureSpec().因为不单单是DecorView需要用测量标准去测量,它的子view,以及它子view的子view都需要自己的一个测量标准去测量,而它们的测量标准获取方法都是一样的,后面会说。
首先来看一下这两个值是怎么得到的:
int
childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int
childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
上面有三个变量mWidth,mHeight,lp.说明一下
mWidth和mHeight这两个值就是当前窗口(手机屏幕)的宽度和高度(减去状态栏的高度).其实测量过程不止一次,在performTraversals()函数一开始就会去测量一次,
DisplayMetrics packageMetrics =
mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
...
// Ask host how big it wants to be
windowSizeMayChange |= measureHierarchy(host, lp, res,
desiredWindowWidth, desiredWindowHeight);
这个时候desiredWindowWidth和desiredWindowHeight是手机屏幕的宽度和高度了。
然后由于是第一次启动该画面,所以会请求WindowManagerService服务重新布局系统中的所有窗口.mWidth和mHeight就是WindowManagerService服务分配给该应用程序窗口的宽度和高度.而这时高度就是手机屏幕的高度减去状态栏的高度了。
这个lp 是WindowManager.LayoutParams类型,相当是DecorView的布局参数,每一个view都有自己的布局参数。
在frameworks/base/core/java/android/view/Window.java中
是Window的成员变量,一路传来到这里的
</pre><pre name="code" class="java" style="font-family: Arial; line-height: 26px;">
// The current window attributes.
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();
看一下它的构造函数
frameworks/base/core/java/android/view/WindowManager.javapublic LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}
它的父类ViewGroup.LayoutParams
frameworks/base/core/java/android/view/ViewGroup.javapublic LayoutParams(int width, int height) {
this.width = width;
this.height = height;
}第二步:getRootMeasureSpec()
在frameworks/base/core/java/android/view/ViewRootImpl.java中
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
// Window can't resize. Force root view to be windowSize.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
// Window can resize. Set max size for root view.
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
// Window wants to be an exact size. Force root view to be that size.
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
这里简单说一下MeasureSpec:
MeasureSpec代表一个32位的int值,高2位代表SpecMode,低30位代表SpecSize,SpecMode是指测量模式,而SpecSize是指在某种测量模式下的规格大小。
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
public static final int EXACTLY = 1 << MODE_SHIFT;
public static final int AT_MOST = 2 << MODE_SHIFT;
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
SpecMode有三类,每一类都表示特殊的含义,如下表示。
UNSPECIFIED
父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
EXACTLY
父容器已经检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。它对应于LayoutParams中match_parent和具体的数值这两种模式。
AT_MOST
父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同的View的具体实现。它对应于LayoutParams中的wrap_contetn.
继续来看getRootMeasureSpec()函数的代码
上面函数不管是获取DecorView宽度的测量标准,还是高度的测量标准,windowSize的是一个常量值
rootDimension的值根据上面LayoutParams的构造函数,知道都是MATCH_PARENT,因此都会执行:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
这么一看DecorView的测量标准是由两个变量决定的,一个是屏幕的宽度(相当于它的父视图),一个是它自己的期望(MATCH_PARENT)。那么其他View的测量标准是由什么决定呢,后面会看到
好了,DecorView的宽度和高度的测量标准都获得了,继续往下看:
第三步:performMeasure()
在frameworks/base/core/java/android/view/ViewRootImpl.java中
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
..
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}第四步:measure()
在frameworks/base/core/java/android/view/View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
..
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
....
}
第五步:onMeasure()
在frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java中
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
final int widthMode = getMode(widthMeasureSpec);
final int heightMode = getMode(heightMeasureSpec);
boolean fixedWidth = false;
if (widthMode == AT_MOST) {
...
}
if (heightMode == AT_MOST) {
...
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
boolean measure = false;
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
if (!fixedWidth && widthMode == AT_MOST) {
...
}
// TODO: Support height?
if (measure) {
..
}
}
final int widthMode = getMode(widthMeasureSpec);
final int heightMode = getMode(heightMeasureSpec);根据第二步知道,这两个值都是EXACTLY
接着super.onMeasure(widthMeasureSpec, heightMeasureSpec);调用它父类的onMeasure()函数,也就是FrameLayout的onMeasure()函数
第六步:onMeasure()
在frameworks/base/core/java/android/widget/FrameLayout.java中
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();//获取decorView有多少个子view
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
final Drawable drawable = getForeground();
if (drawable != null) {
maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
}
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
lp.leftMargin - lp.rightMargin,
MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
if (lp.height == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
lp.topMargin - lp.bottomMargin,
MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
这个函数需要注意一下,不同类型的ViewGroup子类,这个函数的实现不尽相同.
但是函数的作用都是一样的:
1.遍历自己的子view,然后测量每一个子view,如果子view也是ViewGroup,则递归下去.
2.设置自己的测量宽度和测量高度.
首先来看一下测量子view的过程.看measureChildWithMargins()函数
第七步:measureChildWithMargins()
在frameworks/base/core/java/android/view/ViewGroup.java中
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();//首先获取子View的布局参数
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);
}在这个情景,这个函数根据DecorView的测量标准和它的子view的布局参数来确定它的子view的测量标准,然后子view开始自己的测量过程。其他情况也就是普通的ViewGroup的测量标准和它的子view的布局参数来确定它的子view的测量标准.
第八步:getChildMeasureSpec()
在frameworks/base/core/java/android/view/ViewGroup.java中
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);//这个获取的是decorview的测量标准里面的mode
int specSize = MeasureSpec.getSize(spec);//这个获取的是decorview的测量标准里面的值
int size = Math.max(0, specSize - padding);//这个padding包括decorview的padding值 和它子view的margin值
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY://现在decorview的是这种mode
if (childDimension >= 0) {//如果decorview的子view设置了固定值,那么它的测量标准也就定了
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);
}android:layout_width=""
android:layout_height=""
设置成固定值或者MATCH_PARENT,WRAP_CONTENT,FILL_PARENT.
FILL_PARENT和MATCH_PARENT值一样.
总的来说,DecorView的子view测量标准也有两个变量决定的,一个是DecorView(父view)的测量标准,一个是DecorView的子view它自己的布局参数.
也就是说一般View的测量标准是由它父容器的测量标准和它本身的LayoutParams决定。
所以根View DecorView的测量标准获得,与其他普通View的测量标准获得有一定区别。
总结一下上面函数:
一:如果父View的specMode是MeasureSpec.EXACTLY:
①如果子view的布局宽度或高度是常量值,那么子view的SpecSize就是子view的布局宽度或高度的常量值,specMode为MeasureSpec.EXACTLY.
②如果子view的布局宽度或高度是MATCH_PARENT,那么子view的SpecSize就是父view的SpecSize,specMode为MeasureSpec.EXACTLY.。
③如果子view的布局宽度或高度是WRAP_CONTENT,那么子view的SpecSize就是父view的SpecSize,specMode为MeasureSpec.AT_MOST。
二:如果父view的specMode是MeasureSpec.AT_MOST:
①如果子view的布局宽度或高度是常量值,那么子view的SpecSize就是子view的布局宽度或高度的常量值,specMode为MeasureSpec.EXACTLY.
②如果子view的布局宽度或高度是MATCH_PARENT,那么子view的SpecSize就是父view的SpecSize,specMode为MeasureSpec.AT_MOST.。
③如果子view的布局宽度或高度是WRAP_CONTENT,那么子view的SpecSize就是父view的SpecSize,specMode为MeasureSpec.AT_MOST。
三:如果父view的specMode是MeasureSpec.UNSPECIFIED:
①如果子view的布局宽度或高度是常量值,那么子view的SpecSize就是子view的布局宽度或高度的常量值,specMode为MeasureSpec.EXACTLY.
②如果子view的布局宽度或高度是MATCH_PARENT,那么子view的SpecSize就是0,specMode为MeasureSpec.UNSPECIFIED.。
③如果子view的布局宽度或高度是WRAP_CONTENT,那么子view的SpecSize就是0,specMode为MeasureSpec.UNSPECIFIED。
第三情况可以忽略。获取子view的测量标准后回到第七步,子view要根据它的测量标准去测量自己宽度和高度了
<span style="font-size:14px;"> child.measure(childWidthMeasureSpec, childHeightMeasureSpec);</span>
这样就递归下去了,如果该child是ViewGroup的话,则又会调用它的onMeasure()函数,然后一个个去获取它子view的测量标准,函数也是getChildMeasureSpec(),接着它的子view调用自己的measure()函数去测量,这样一直递归到一个具体的view,在该view的onMeasure()函数里调用setMeasuredDimension()来设置自己测量宽度和高度了,
第九步:setMeasuredDimension()
在frameworks/base/core/java/android/view/view.java中
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
onMeasure()的两个参数也就是该View的宽度和高度的测量标准了.
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}view的测量宽度和高度就是getDefaultSize()函数的返回值了
首先来看一下getSuggestedMinimumWidth()函数
第十步:getSuggestedMinimumWidth()
在frameworks/base/core/java/android/view/view.java中
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}如果该View没有设置背景,那么返回mMinWidth,而mMinWidth对应于android:minWidth这个属性所指定的值,这个属性如果不指定,那么mMinWidth为0,如果view指定了背景,则返回mMinWidth
: max(mMinWidth, mBackground.getMinimumWidth());那么mBackground.getMinimumWidth()是什么呢?我们看一下Drawable的getMinimumWidth()函数
public int getMinimumWidth() {
final int intrinsicWidth = getIntrinsicWidth();
return intrinsicWidth > 0 ? intrinsicWidth : 0;
}可以看出,getMinimumWidth返回的就是Drawable的原始宽度,前提是这个Drawable有原始宽度,否则就返回0.那么Drawable在什么情况下有原始高度呢,这里举个例子说明一下,ShapeDrawable无原始宽/高,而BitmapDrawable有原始宽/高(图片的尺寸),以后有机会再讲。因为获取宽度和高度的原理一样,就没看了
第十一步:getDefaultSize()
在frameworks/base/core/java/android/view/view.java中
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED://这种情况才与上面第九步有关
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}具体view在基类View中onMeasure()函数里都有了默认的实现。就是上面第九步至第十一步
而像DecorView这样的ViewGroup重写该onMeasure()函数,第五步第六步,这是因为ViewGroup还需要去遍历它的子view,让它子view又去测量它们自己,而且不同类型的ViewGroup子类有不同的布局特性,这导致它们的测量细节各不相同,如LinerLayout和FrameLayout这两者的布局特性显然不同。
但是viewgroup递归测量完自己的子view后,也要通过setMeasuredDimension()的设置自己测量宽度和高度.回到第六步DecorView在递归测量完自己的子view后,要设置自己测量宽度和高度
在frameworks/base/core/java/android/view/view.java中
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
在frameworks/base/core/java/android/view/ViewGroup.java中
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) {
if (Build.VERSION.SDK_INT >= 11) {
return View.resolveSizeAndState(size, measureSpec, childMeasuredState);
}
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result | (childMeasuredState&MEASURED_STATE_MASK);
}至于它的onMeasure()其他细节不再分析了,大家有兴趣可以去分析源码。
测量过程可能要重复几次.
上面就是测量的过程了.
标签:
原文地址:http://blog.csdn.net/hehe26/article/details/51910845