FrameLayout 測量過程(程式碼流水線)
阿新 • • 發佈:2019-02-03
/**
* {@inheritDoc}
* 如果,子 view如果設定屬性是 match_parent,
* 如果,measureMatchParentChildren是true (其實就是framlayout模式是非精準模式)
* 那麼這種子view是被測量了兩次
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
//當FrameLayout的寬和高 只有同時設定為 match_parent
//當FrameLayout的寬和高 指定的size
///measureMatchParentChlidren = false,否則為true。
//MeasureSpec.EXACTLY時,就表示當前檢視的大小等於引數measureSpec中所指定的值。
//當FrameLayout 非精準模式,這時候要把所有寬高設定為 //match_parent的子View都記錄下來
// 這時候FrameLayout的寬高同時受子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) { //一次遍歷每一個不為GONE的子view
//第一次測量孩子
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());
//的mMatchParentChlidren的list裡存的是設定為match_parent的子view。
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
//加上父親的padidng
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));
//上面的測量,得到了framlayout的寬,高,下面,需要根據寬高,重新測量子view - 主要是 match_parent 屬性的孩子
//子view中設定為match_parent的個數
//重新測量
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();
final int childWidthMeasureSpec;
//將屬性為match_parent的再測量一遍
if (lp.width == LayoutParams.MATCH_PARENT) {//子view的寬是match_parent,則寬度期望值是總寬度-padding-margin
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
//將屬性為match_parent的再測量一遍
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
//子view的寬是match_parent,則寬度期望值是總寬度-padding-margin
// //把這部分子view重新計算大小
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//根據parentWidthMeasureSpec,ViewGroup剩餘寬度,child的寬度重新計算child的widthMeasureSpec
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);
}
/**
* @param spec The requirements for this view
* parentHeightMeasureSpec
* @param padding The padding of this view for the current dimension and
* margins, if applicable
* 父親已經用的 + 父親的pading + 父親的margin
* @param childDimension How big the child wants to be in the current
* dimension
* @return a MeasureSpec integer for the child
* 孩子的佈局引數
*/
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
//得到父親的測量模式和大小
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
//父親大小 - 父親的冗餘
//當前父View還剩的空間大小
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:// 父View強制設定了一個大小給子View
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) {//大小設定為父親剩餘空間大小,意味著讓子View去處理,子View最大的大小不能超過父View
// 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:// 父View給子View一個限定的大小,子View不能超過這個大小
if (childDimension >= 0) {// 子View的size設定成XML中配置的大小
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {// 子View的大小為父View的size 意味著讓子View去處理,子View最大的大小不能超過父View
// 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:// 意味著子View要多大都可以
if (childDimension >= 0) {//子View的size設定成XML中配置的大小
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;//意味著,我可以給讓你想顯示多大就顯示多大,但是在XML中或者addView的時候, 寫死了childDimension,那麼它的大小就這麼大吧
} else if (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;
} 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;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}