[セットトップ]Android Viewの描画メカニズムをソースコードの観点から分析する(一)
29866 ワード
Androidの学習の道では、誰もがAndroidのソースコードをめくることが避けられない.ソースコードの角度から問題を分析してこそ、Android開発を本当に遊ぶことができるからだ.最近仕事が暇なので、何か书きたいと思って、ちょうど自分で整理してもいいです.ビューの表示メカニズムがカスタムビューの基礎であり、面接でよく聞かれる質問でもあることを考慮して、この文を記録し、皆さんと共有し、レベルが限られているため、皆さんがレンガをたたいてくれることを望んでいます.感謝に堪えません.カスタマイズされたviewがある同業者は、viewの表示がactivityのsetContentViewメソッドに依存してPhoneWindowフォームに依存していることを知っておくべきで、表示の過程で、このviewは測定(measure)、レイアウト(layout)、draw(描画)の3段階を経験し、measure段階は各Viewの大きさを得ることである.Layoutフェーズは各ViewのUI上の座標を計算し、drawフェーズは前の2つのフェーズのデータに基づいてUI描画を行う.描画が完了すると、インタフェースに表示されます.
measure:
xmlがuiに表示されると、layoutのidをactivityのsetContentViewに転送し、最終的にはViewRootのperformTraversalsメソッドを呼び出し、viewの描画を担当します.
getRootMeasureSpecに入力されるパラメータの1つはlpのプロパティであり、lpの定義は次のとおりです.
つまりlpを意味しますwidthとlp.heightの値はすべてmatch_parent. getRootMeasureSpecメソッドでは、windowSizeとrootDimensionに従って測定仕様を返します.lp.widthは、例えばdecorViewのwidthです.widthの値がMATCH_の場合PARENT、では戻ってくる計測規格はwindowSize+MeasureSpecです.EXACTLYの値は、WRAP_CONTENTはwindowSize+MeasureSpecです.AT_MOSTの値.
Windows SizeとrootDimensionでマッピングされたmode値を加算する理由については、次のコードを参照してください.
MeasureSpec.makeMeasureSpecでは単純にsizeとmodeを加算して返すだけです.ここで、このような戻り値は、AT_MOST、EXACTLYはmodeを表しており、いずれも2ビットを占有しておりint値であり、int値はjavaで32ビットを占有しているため、他の30ビットはAndroidでsizeとして設計されている.すなわち、各高2ビットはspecModeを表し、低30ビットはサイズの大きさを表す.前の操作が完了するとhostの呼び出しが開始されます.measure操作でviewのサイズを計算します.hostはDecorViewを表し、decorViewはFrameLayoutつまりView Groupであるが、ViewではViewから継承され、viewのmeasureメソッドは書き換えられないのでhost.measureはやはりviewのmeasureメソッドで、このメソッドではViewGroupのonMeasure(widthMeasureSpec,heightMeasureSpec)メソッドを呼び出しているので、FrameLayoutのonMeasureメソッドに移動します
このメソッドにはmeasureChildWithMarginsメソッドが呼び出されていますが、実はView Groupのサブクラスは測定時にこのメソッドを実行し、最後にViewクラスのmeasureメソッドを呼び出して測定します.プロトタイプは以下の通りです.
ビューのmeasureメソッドの2つのパラメータは、親ビューが最終的に使用するフォームサイズが親ビューと子ビューで共に決定されるため、幅と高さのmeasureSpecに対応します.したがってchildWidthMeasureSpecは、親ビューがサブビューに渡される推奨値です.サイズを測定する場合、paddingとmarginの値もサイズの一部とします.上記はviewの測定過程だけです.LinearLayoutの測定過程を分析します.LinearLayoutの測定では、以下が主なコードです.
測定の過程で、すべてのサブビューを巡回し、すべてのサブビューの総高さとtotalWeightを計算し、各サブビューに対してmeasure操作を行う.サブビューを巡るchildの過程でchildがNullであればmTotalLengthに0を加え、childが見えない場合は何もせずに次のサブビューを巡り続け、childのlayoutParamsを手に入れ、childのlayout_を取り出すWeight、totalWeightにweight値を追加します.次に、親ビューが提供する高さmodeがMeasureSpecに等しいと判断する.EXACTLYで、childのheightが0に等しく、childのweightの値が0より大きい場合は、このchildを測定しない.weightがあるので、すべてのchildを測定した後、weightの値に基づいて高さを割り当てるが、ここではchildのmargin値を加算しなければならない.weightがどのように変わっても、margin値は影響しないからだ.mTotalLength=Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin).ここでmaxを取るのはchildのmarginが負数である可能性があるからです.前の条件を満たさない場合modeはMeasureSpecに等しい.EXACTLYは、childを測定する必要があり、測定を経てchildの高さを知ることができ、この高さをmTotalLengthに加えます.この条件では、childのheightが0に等しく、childのweightの値が0より大きい場合、現在のviewのheightをLayoutParamsに設定する.WRAP_CONTENT、それから測定します.今までは全体の高さを測っただけで、まだweightに従って高さを分配していません.ははは、こんなに多くの読者が気絶しそうだと言っていましたが、大丈夫です.weightの分配について、ソースコードを集めて例を挙げてお話しします(次の文章を参照).次にmTotalLengthに親ビューのpaddingTopとpaddingBottom値を加えてheightSizeを得ます.ただし、各ビューにはバックグラウンドがある、バックグラウンドには高さがあるので、heightSizeとgetSuggestedMinimumHeight()の値の大きさを比較し、最大値をheightSize、getSuggestedMinimumHeight()が返すmBGDrawableとする.getMinimumHeight()はdrawableバックグラウンドの最小高さです.親ビューのheightMeasureSpecのmodeが正確であれば、このheightSizeの最終値も親ビューで指定されたサイズであるべきであるため、resolveSizeによって再びheightSizeに値を割り当てます.次にweightに従って高さを割り当て始めます.高さを割り当てる前に、残りの高さheightSize-mTotalLengthが0ではなく、totalWeightが0より大きいことを保証します.次にサブviewを巡回してサブviewのweightを得,weightが0より大きい場合,式:childのweight属性値*残高/weight総和によりchildが残高からどれだけの高さ値を得るかを算出し,残高値をリフレッシュしchildに高さを再設定して測定する.最後にすべての子供のView測定が完了し、setMeasuredDimensionを呼び出して自分のサイズを設定します.
measure:
xmlがuiに表示されると、layoutのidをactivityのsetContentViewに転送し、最終的にはViewRootのperformTraversalsメソッドを呼び出し、viewの描画を担当します.
private void performTraversals() {
final View host = mView;
if (DBG) {
host.debug();
}
if (host == null || !mAdded)
return;
......
Rect frame = mWinFrame;
if (mFirst) {
fullRedrawNeeded = true;
mLayoutRequested = true;
DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
......
}
........
boolean insetsChanged = false;
if (mLayoutRequested) {
getRunQueue().executeActions(attachInfo.mHandler);
if (mFirst) {
host.fitSystemWindows(mAttachInfo.mContentInsets);
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
mAttachInfo.mContentInsets.set(mPendingContentInsets);
host.fitSystemWindows(mAttachInfo.mContentInsets);
insetsChanged = true;
}
if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
}
if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowResizesToFitContent = true;
DisplayMetrics packageMetrics = mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
}
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
...........
}
getRootMeasureSpecに入力されるパラメータの1つはlpのプロパティであり、lpの定義は次のとおりです.
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
WindowManager.LayoutParams lp = mWindowAttributes;
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
format = PixelFormat.OPAQUE;
}
つまりlpを意味しますwidthとlp.heightの値はすべてmatch_parent. getRootMeasureSpecメソッドでは、windowSizeとrootDimensionに従って測定仕様を返します.lp.widthは、例えばdecorViewのwidthです.widthの値がMATCH_の場合PARENT、では戻ってくる計測規格はwindowSize+MeasureSpecです.EXACTLYの値は、WRAP_CONTENTはwindowSize+MeasureSpecです.AT_MOSTの値.
private 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;
}
Windows SizeとrootDimensionでマッピングされたmode値を加算する理由については、次のコードを参照してください.
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) {
return size + mode;
}
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
..........
}
MeasureSpec.makeMeasureSpecでは単純にsizeとmodeを加算して返すだけです.ここで、このような戻り値は、AT_MOST、EXACTLYはmodeを表しており、いずれも2ビットを占有しておりint値であり、int値はjavaで32ビットを占有しているため、他の30ビットはAndroidでsizeとして設計されている.すなわち、各高2ビットはspecModeを表し、低30ビットはサイズの大きさを表す.前の操作が完了するとhostの呼び出しが開始されます.measure操作でviewのサイズを計算します.hostはDecorViewを表し、decorViewはFrameLayoutつまりView Groupであるが、ViewではViewから継承され、viewのmeasureメソッドは書き換えられないのでhost.measureはやはりviewのmeasureメソッドで、このメソッドではViewGroupのonMeasure(widthMeasureSpec,heightMeasureSpec)メソッドを呼び出しているので、FrameLayoutのonMeasureメソッドに移動します
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int count = getChildCount();
int maxHeight = 0;
int maxWidth = 0;
// Find rightmost and bottommost child
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
}
}
// Account for padding too
maxWidth += mPaddingLeft + mPaddingRight + mForegroundPaddingLeft + mForegroundPaddingRight;
maxHeight += mPaddingTop + mPaddingBottom + mForegroundPaddingTop + mForegroundPaddingBottom;
// 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(resolveSize(maxWidth, widthMeasureSpec),
resolveSize(maxHeight, heightMeasureSpec));
}
このメソッドにはmeasureChildWithMarginsメソッドが呼び出されていますが、実はView Groupのサブクラスは測定時にこのメソッドを実行し、最後にViewクラスのmeasureメソッドを呼び出して測定します.プロトタイプは以下の通りです.
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);
}
ビューのmeasureメソッドの2つのパラメータは、親ビューが最終的に使用するフォームサイズが親ビューと子ビューで共に決定されるため、幅と高さのmeasureSpecに対応します.したがってchildWidthMeasureSpecは、親ビューがサブビューに渡される推奨値です.サイズを測定する場合、paddingとmarginの値もサイズの一部とします.上記はviewの測定過程だけです.LinearLayoutの測定過程を分析します.LinearLayoutの測定では、以下が主なコードです.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
/** * */
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
mTotalLength = 0;
int maxWidth = 0;
int alternativeMaxWidth = 0;
int weightedMaxWidth = 0;
boolean allFillParent = true;
float totalWeight = 0;
final int count = getVirtualChildCount();
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
boolean matchWidth = false;
final int baselineChildIndex = mBaselineAlignedChildIndex;
final boolean useLargestChild = mUseLargestChild;
int largestChildHeight = Integer.MIN_VALUE;
// See how tall everyone is. Also remember max width.
// View, View , View measure
// view
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
// // child Null, mTotalLength 0
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == View.GONE) {
//// child ,
i += getChildrenSkipCount(child, i);
continue;
}
// child LayoutParams
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
// weight totalWeight,weight xml layout_weight
totalWeight += lp.weight;
if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
/** View mode EXACTLY, height==0 lp.weight>0 measure child, topMargin bottoMargin totaoLength */
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
} else {
int oldHeight = Integer.MIN_VALUE;
// View EXACLTY, lp.height == 0 && lp.weight > 0, View height WRAP_CONTENT
if (lp.height == 0 && lp.weight > 0) {
oldHeight = 0;
lp.height = LayoutParams.WRAP_CONTENT;
}
measureChildBeforeLayout(
child, i, widthMeasureSpec, 0, heightMeasureSpec, totalWeight == 0 ? mTotalLength : 0);
if (oldHeight != Integer.MIN_VALUE) {
lp.height = oldHeight;
}
final int childHeight = child.getMeasuredHeight();
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
lp.bottomMargin + getNextLocationOffset(child));
if (useLargestChild) {
largestChildHeight = Math.max(childHeight, largestChildHeight);
}
}
if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
mBaselineChildTop = mTotalLength;
}
.....................
final int margin = lp.leftMargin + lp.rightMargin;
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);
allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
if (lp.weight > 0) {
weightedMaxWidth = Math.max(weightedMaxWidth,
matchWidthLocally ? margin : measuredWidth);
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
matchWidthLocally ? margin : measuredWidth);
}
i += getChildrenSkipCount(child, i);
}
if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child == null) {
mTotalLength += measureNullChild(i);
continue;
}
if (child.getVisibility() == GONE) {
i += getChildrenSkipCount(child, i);
continue;
}
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
child.getLayoutParams();
// Account for negative margins
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
}
// padding
mTotalLength += mPaddingTop + mPaddingBottom;
// View heightSize
int heightSize = mTotalLength;
// Check against our minimum height
// mBGDrawable.getMinimumHeight()
heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
// heightSize , LinearLayout xml , Activity
// heightSize , ,
// Reconcile our calculated size with the heightMeasureSpec
// heightMeasureSpec size , heightsize ==heightMeasureSpec size,
heightSize = resolveSize(heightSize, heightMeasureSpec);
// Either expand children with weight to take up available space or
// shrink them if they extend beyond our current bounds
// delta
int delta = heightSize - mTotalLength;
if (delta != 0 && totalWeight > 0.0f) {
// weightsum , weightSum weightsum , totalWeight
float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;
mTotalLength = 0;
for (int i = 0; i < count; ++i) {
final View child = getVirtualChildAt(i);
if (child.getVisibility() == View.GONE) {
continue;
}
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
float childExtra = lp.weight;
if (childExtra > 0) {
// Child said it could absorb extra space -- give him his share
// : view weight * / weight
int share = (int) (childExtra * delta / weightSum);
weightSum -= childExtra;
delta -= share;
final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
mPaddingLeft + mPaddingRight +
lp.leftMargin + lp.rightMargin, lp.width);
// TODO: Use a field like lp.isMeasured to figure out if this
// child has been previously measured
if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
// child was measured once already above...
// base new measurement on stored values
// view
int childHeight = child.getMeasuredHeight() + share;
if (childHeight < 0) {
childHeight = 0;
}
child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
} else {
// child was skipped in the loop above.
// Measure for this first time here
child.measure(childWidthMeasureSpec,
MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
MeasureSpec.EXACTLY));
}
}
final int margin = lp.leftMargin + lp.rightMargin;
final int measuredWidth = child.getMeasuredWidth() + margin;
maxWidth = Math.max(maxWidth, measuredWidth);
boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
lp.width == LayoutParams.MATCH_PARENT;
alternativeMaxWidth = Math.max(alternativeMaxWidth,
matchWidthLocally ? margin : measuredWidth);
allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
}
// Add in our padding
mTotalLength += mPaddingTop + mPaddingBottom;
// TODO: Should we recompute the heightSpec based on the new total length?
} else {
alternativeMaxWidth = Math.max(alternativeMaxWidth,
weightedMaxWidth);
}
if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
maxWidth = alternativeMaxWidth;
}
maxWidth += mPaddingLeft + mPaddingRight;
// Check against our minimum width
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// View ,
setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec), heightSize);
if (matchWidth) {
forceUniformWidth(count, heightMeasureSpec);
}
}
測定の過程で、すべてのサブビューを巡回し、すべてのサブビューの総高さとtotalWeightを計算し、各サブビューに対してmeasure操作を行う.サブビューを巡るchildの過程でchildがNullであればmTotalLengthに0を加え、childが見えない場合は何もせずに次のサブビューを巡り続け、childのlayoutParamsを手に入れ、childのlayout_を取り出すWeight、totalWeightにweight値を追加します.次に、親ビューが提供する高さmodeがMeasureSpecに等しいと判断する.EXACTLYで、childのheightが0に等しく、childのweightの値が0より大きい場合は、このchildを測定しない.weightがあるので、すべてのchildを測定した後、weightの値に基づいて高さを割り当てるが、ここではchildのmargin値を加算しなければならない.weightがどのように変わっても、margin値は影響しないからだ.mTotalLength=Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin).ここでmaxを取るのはchildのmarginが負数である可能性があるからです.前の条件を満たさない場合modeはMeasureSpecに等しい.EXACTLYは、childを測定する必要があり、測定を経てchildの高さを知ることができ、この高さをmTotalLengthに加えます.この条件では、childのheightが0に等しく、childのweightの値が0より大きい場合、現在のviewのheightをLayoutParamsに設定する.WRAP_CONTENT、それから測定します.今までは全体の高さを測っただけで、まだweightに従って高さを分配していません.ははは、こんなに多くの読者が気絶しそうだと言っていましたが、大丈夫です.weightの分配について、ソースコードを集めて例を挙げてお話しします(次の文章を参照).次にmTotalLengthに親ビューのpaddingTopとpaddingBottom値を加えてheightSizeを得ます.ただし、各ビューにはバックグラウンドがある、バックグラウンドには高さがあるので、heightSizeとgetSuggestedMinimumHeight()の値の大きさを比較し、最大値をheightSize、getSuggestedMinimumHeight()が返すmBGDrawableとする.getMinimumHeight()はdrawableバックグラウンドの最小高さです.親ビューのheightMeasureSpecのmodeが正確であれば、このheightSizeの最終値も親ビューで指定されたサイズであるべきであるため、resolveSizeによって再びheightSizeに値を割り当てます.次にweightに従って高さを割り当て始めます.高さを割り当てる前に、残りの高さheightSize-mTotalLengthが0ではなく、totalWeightが0より大きいことを保証します.次にサブviewを巡回してサブviewのweightを得,weightが0より大きい場合,式:childのweight属性値*残高/weight総和によりchildが残高からどれだけの高さ値を得るかを算出し,残高値をリフレッシュしchildに高さを再設定して測定する.最後にすべての子供のView測定が完了し、setMeasuredDimensionを呼び出して自分のサイズを設定します.