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);
-
}
主要調用了getChildMeasureSpec()方法
getChildMeasureSpec
代碼如下
-
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
-
// 以寬度爲例,傳入的參數分別是:父view的widthMeasureSpec、已用寬度(子view左右內外間距+已用的間距)、子view要求的寬度
-
int specMode = MeasureSpec.getMode(spec);
-
int specSize = MeasureSpec.getSize(spec);
-
int size = Math.max(0, specSize - padding); // 父寬度 - 子view的內外間距
-
// 也就是子view的最大寬度
-
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. sdk < 23時,sUseZeroUnspecifiedMeasureSpec爲true
-
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
-
resultMode = MeasureSpec.UNSPECIFIED;
-
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
-
// Child wants to determine its own size.... find out how
-
// big it should be.
-
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
-
resultMode = MeasureSpec.UNSPECIFIED;
-
}
-
break;
-
}
-
//noinspection ResourceType
-
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
-
}
combineMeasuredState
-
public static int combineMeasuredStates(int curState, int newState) {
-
return curState | newState;
-
}
沒啥好說的,合併兩個state
getMeasuredState
-
public final int getMeasuredState() {
-
return (mMeasuredWidth&MEASURED_STATE_MASK)
-
| ((mMeasuredHeight>>MEASURED_HEIGHT_STATE_SHIFT)
-
& (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
-
}
通過位運算合併高度和寬度的state,結果就是第1個字節是寬度的state,第3個字節是高度的state
resolveSizeAndState
-
public static int resolveSizeAndState(int size, int measureSpec, int childMeasuredState) { // size是自己需要的尺寸,measureSpec是父view指定的尺寸
-
final int specMode = MeasureSpec.getMode(measureSpec);
-
final int specSize = MeasureSpec.getSize(measureSpec); // 父view指定的尺寸
-
final int result;
-
switch (specMode) {
-
case MeasureSpec.AT_MOST:
-
if (specSize < size) {
-
result = specSize | MEASURED_STATE_TOO_SMALL; // 設定標誌位,表示父view得分配的再大一些
-
} else {
-
result = size;
-
}
-
break;
-
case MeasureSpec.EXACTLY:
-
result = specSize;
-
break;
-
case MeasureSpec.UNSPECIFIED:
-
default:
-
result = size;
-
}
-
return result | (childMeasuredState & MEASURED_STATE_MASK);
-
}
最後保存狀態時,由於childMeasureState是第一個字節是寬度的狀態,第三個字節是高度的狀態,所以再測量寬度高度時調用此方法,要對childState進行位運算
setMeasuredDimension
這個是用來保存當前view尺寸的,當我們使用自定義view並且覆寫了onMeasure()方法時,不在最後調用這個方法,直接就報錯了
-
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
-
boolean optical = isLayoutModeOptical(this);
-
if (optical != isLayoutModeOptical(mParent)) { // layout_mode是LAYOUT_MODE_OPTICAL的情況很少出現,所以測量過程中涉及optical的,我們一般都可以直接忽略不計
-
Insets insets = getOpticalInsets();
-
int opticalWidth = insets.left + insets.right;
-
int opticalHeight = insets.top + insets.bottom;
-
measuredWidth += optical ? opticalWidth : -opticalWidth;
-
measuredHeight += optical ? opticalHeight : -opticalHeight;
-
}
-
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
-
}
直接調用了setMeasuredDimensionRaw()方法
-
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
-
mMeasuredWidth = measuredWidth;
-
mMeasuredHeight = measuredHeight;
-
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
-
}
嗯,就是這一步,保存了measuredHeight和measuredWidth.不過,這裏要說明一下,measuredHeight/Width高八位是狀態,低二十四位纔是真正的尺寸,這也就是View.getMeasuredWidth/Height和View.getMeasuredWidth/HeightAndState()方法的區別所在
getDefaultSize
-
public static int getDefaultSize(int size, int measureSpec) {
-
int result = size; // size是view的默認尺寸
-
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;
-
}
getSuggestMinimunHeight/Width
以高度爲例,代碼如下
-
protected int getSuggestedMinimumHeight() {
-
return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());
-
}
涉及view的背景,mBackground是Drawable類的,它的getMinimumHeight()由不同的子類分別實現,反正就是返回最小的高度,然後再跟view本身的最小高度取最大值(view本身的最小高度不手動設置的話,就是0)