/** * Figures out the measure spec for the root view in a window based on it's * layout params. * * @param windowSize * The available width or height of the window * * @param rootDimension * The layout params for one dimension (width or height) of the * window. * * @return The measure spec to use to measure the root view. */privatestaticintgetRootMeasureSpec(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;
}
複製代碼
/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding * and margins. The child must have MarginLayoutParams The heavy lifting is * done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param widthUsed Extra space that has been used up by the parent * horizontally (possibly by other children of the parent) * @param parentHeightMeasureSpec The height requirements for this view * @param heightUsed Extra space that has been used up by the parent * vertically (possibly by other children of the parent) */protectedvoidmeasureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed){
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
finalint childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
finalint childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
複製代碼
/** * Does the hard part of measureChildren: figuring out the MeasureSpec to * pass to a particular child. This method figures out the right MeasureSpec * for one dimension (height or width) of one child view. * * The goal is to combine information from our MeasureSpec with the * LayoutParams of the child to get the best possible results. For example, * if the this view knows its size (because its MeasureSpec has a mode of * EXACTLY), and the child has indicated in its LayoutParams that it wants * to be the same size as the parent, the parent should ask the child to * layout given an exact size. * * @param spec The requirements for this view * @param padding The padding of this view for the current dimension and * margins, if applicable * @param childDimension How big the child wants to be in the current * dimension * @return a MeasureSpec integer for the child */publicstaticintgetChildMeasureSpec(int spec, int padding, int childDimension){
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
//padding,就是已被佔用的空間,就是 父容器的padding+child的marging//size,是ViewGroup自己size減去已使用的空間,是ViewGroup能提供給child的最大值。int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on uscase MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} elseif (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} elseif (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 uscase MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} elseif (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;
} elseif (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 becase MeasureSpec.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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} elseif (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 ResourceTypereturn MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
複製代碼
/** * <p> * This is called to find out how big a view should be. The parent * supplies constraint information in the width and height parameters. * </p> * * <p> * The actual measurement work of a view is performed in * {@link #onMeasure(int, int)}, called by this method. Therefore, only * {@link #onMeasure(int, int)} can and must be overridden by subclasses. * </p> * * * @param widthMeasureSpec Horizontal space requirements as imposed by the * parent * @param heightMeasureSpec Vertical space requirements as imposed by the * parent * * @see #onMeasure(int, int) */publicfinalvoidmeasure(int widthMeasureSpec, int heightMeasureSpec){
...
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
複製代碼
publicstaticintgetDefaultSize(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;
}
複製代碼
/** * Ask all of the children of this view to measure themselves, taking into * account both the MeasureSpec requirements for this view and its padding. * We skip children that are in the GONE state The heavy lifting is done in * getChildMeasureSpec. * * @param widthMeasureSpec The width requirements for this view * @param heightMeasureSpec The height requirements for this view */protectedvoidmeasureChildren(int widthMeasureSpec, int heightMeasureSpec){
finalint size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
複製代碼
@OverrideprotectedvoidonMeasure(int widthMeasureSpec, int heightMeasureSpec){
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
複製代碼
繼續看measureVertical():
voidmeasureVertical(int widthMeasureSpec, int heightMeasureSpec){
...
//下面這句官方註釋:看每一個人多高,也記住最大寬度。想一想這不就是計算豎向LinearLayout寬高的思路嘛!// See how tall everyone is. Also remember max width.for (int i = 0; i < count; ++i) {
...
final View child = getVirtualChildAt(i);
...
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
...
// Determine how big this child would like to be. If this or// previous children have given a weight, then we allow it to// use all available space (and we will shrink things later// if needed).finalint usedHeight = totalWeight == 0 ? mTotalLength : 0;
//這裏測量child(裏面就是measureChildWithMargins())
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
heightMeasureSpec, usedHeight);
finalint childHeight = child.getMeasuredHeight();
...
finalint totalLength = mTotalLength;
//這裏mTotalLength加上child的高度、margin,就是child高度累積。
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
...
//這裏記錄最大寬度(包含margin)finalint margin = lp.leftMargin + lp.rightMargin;
finalint measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);
...
}
//遍歷完了:高度加上自身的上下padding// Add in our padding
mTotalLength += mPaddingTop + mPaddingBottom;
int heightSize = mTotalLength;
// Check against our minimum height
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
//這裏很重要:調用resolveSizeAndState--決定 計算的高度(高度累加)和 LinearLayout的父容器約束的高度,取哪個。// Reconcile our calculated size with the heightMeasureSpecint heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
...
//最大寬度加上左右margin
maxWidth += mPaddingLeft + mPaddingRight;
// Check against our minimum width
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
//設置最終的測量尺寸(寬也也一樣調用resolveSizeAndState決定取哪一個)
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
heightSizeAndState);
}
複製代碼
/** * Utility to reconcile a desired size and state, with constraints imposed * by a MeasureSpec. Will take the desired size, unless a different size * is imposed by the constraints. The returned value is a compound integer, * with the resolved size in the {@link #MEASURED_SIZE_MASK} bits and * optionally the bit {@link #MEASURED_STATE_TOO_SMALL} set if the * resulting size is smaller than the size the view wants to be. * * @param size How big the view wants to be. --想要的尺寸 * @param measureSpec Constraints imposed by the parent. --父佈局給的measureSpec * @param childMeasuredState Size information bit mask for the view's * children. * @return Size information bit mask as defined by * {@link #MEASURED_SIZE_MASK} and * {@link #MEASURED_STATE_TOO_SMALL}. */publicstaticintresolveSizeAndState(int size, int measureSpec, int childMeasuredState){
finalint specMode = MeasureSpec.getMode(measureSpec);
finalint specSize = MeasureSpec.getSize(measureSpec);
finalint result;
switch (specMode) {
case MeasureSpec.AT_MOST:
//AT_MOST時,想要的尺寸大於約束的尺寸,就只能取 約束的尺寸。if (specSize < size) {
result = specSize | MEASURED_STATE_TOO_SMALL;
} else {
result = size;
}
break;
case MeasureSpec.EXACTLY:
//dp值、match_parent且父EXACTLY,就是SpecSize
result = specSize;
break;
case MeasureSpec.UNSPECIFIED:
default:
result = size;
}
return result | (childMeasuredState & MEASURED_STATE_MASK);
}
複製代碼
privatevoidsetChildFrame(View child, int left, int top, int width, int height){
//這裏width、height就是 上面獲取的 測量寬高
child.layout(left, top, left + width, top + height);
}
複製代碼