《Android内核剖析》,《Android开发艺术探索》
- MeasureSpec介绍
- 将SpecMode与SpecSize打包得到int值,MeasureSpec也提供了解包方法来分别获取SpecMode与SpecSize.
- SpecSize:视图大小值
- SpecMode: 测量模式
- EXACTLY:确定的,父视图希望子视图大小应该是MeasureSpec中SpecSize值.
- AT_MOST:最多,子视图大小最多是MeasureSpec中SpecSize值.
- UNSPECIFIED:没有限制,子视图可以根据自身特性设置大小.
// View.MeasureSpec.java
public static class MeasureSpec {
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;
// 打包获得int值
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
// 解包获取SpecMode
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
// 解包获取SpecSize
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
}
- DecorView如何measure.
- 获取父容器的MeasureSpec
// ViewRootImpl.java
public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
// WindowManager.LayoutParams默认宽高是MATCH_PARENT.
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
public ViewRootImpl(Context context, Display display) {
// 第一次添加视图时候为true
mFirst = true;
}
private void performTraversals() {
WindowManager.LayoutParams lp = mWindowAttributes;
// 如果是第一次添加视图
if (mFirst) {
...
if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL|| lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
// 这里测量状态栏这些尺寸
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
// 这里就是测量屏幕尺寸了
DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
...
} else {
...
}
//执行布局
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {
measureHierarchy(host, lp, mView.getContext().getResources(),desiredWindowWidth, desiredWindowHeight);
}
private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp, final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {
// 这里开始创建屏幕的MeasureSpec,给DecorView测量用.
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
// 调用DecorView父类的measure()
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
try {
// 对DecorView进行测量
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
}
}
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
// rootDimension 是MATCH_PARENT.
case ViewGroup.LayoutParams.MATCH_PARENT:
// 传入DecorView中的MeasureSpec是这个
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
}
- 调用DecorView.measure()
// DecorView父类是FrameLayout,不存在measure()方法.
// FrameLayout父类是ViewGroup,不存在measure()方法.
// 只有View中存在measure()方法.
public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {
// 当调用DecorView.measure(),最先走View.measure().
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
// DecorView中重写了,所以会走 DecorView.onMeasure()方法.
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// 但是最终会走这里,为DecorView设置真实的尺寸
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
// 返回View建议使用的最小尺寸
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
// size代表此时DecorView建议最小尺寸
// measureSpec代表屏幕对DecorView测量模式和建议尺寸.
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:// 这是ViewRootImpl为DecorView创建的测量模式
result = specSize;// 所以DecorView最终尺寸使用的是ViewRootImpl提供的建议尺寸,到这里DecorView的测量就完成了.
break;
}
return result;
}
}
// PhoneWindow.DecorView.java
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
// 走到这里之后最终会调用View.onMeasure()
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
}
- View如何measure.
public class View implements Drawable.Callback, KeyEvent.Callback,AccessibilityEventSource {
// 接收到父控件传递过来的MeasureSpec
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
..
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) {
...
if (cacheIndex < 0 || sIgnoreMeasureCache) {
...
// 看这里
onMeasure(widthMeasureSpec, heightMeasureSpec);
} else {
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
}
}
...
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 设置尺寸
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
...
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
// 设置View大小
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
...
}
// 根据不同的measureSpec以及期望大小返回不同的值.
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;
}
}
- ViewGroup中默认measure.
- ViewGroup中含有很多子View,而View.measure()也是在ViewGruop中调用的.先来看下ViewGroup中如何确定传递给子View的measureSpec.
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
// spec:父控件传递给ViewGroup的MeasureSpec
// padding: ViewGroup已经占用的控件尺寸
// childDimension:子控件想要的尺寸,一般写在xml中
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) {
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
// 根据不同的测量模式测量值还有子View想要的值得到一个子View的MeasureSpec.
// 子View的MeasureSpec获取有规律,可以用一个图表示.
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
}
ViewGruop中获取创建子View的MeasureSpec规则
- ViewGroup默认测量子View的方法
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
// 默认测量子View的方法
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
// 获取子View
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
// 测量子View
// widthMeasureSpec,heightMeasureSpec:ViewGroup中的MeasureSpec
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
// 测量子View
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
// 获取子View布局参数
final LayoutParams lp = child.getLayoutParams();
// 用这些条件去获取子View的MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop + mPaddingBottom, lp.height);
// 调用子View.measure();
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}