RelativeLayout和LinearLayout效能比較 相對佈局和線性佈局的效能比較
看到幾篇關於RelativeLayout和LinearLayout效能分析的部落格,寫的相當不錯,這裡在大神的基礎上,增加了部分內容 RelativeLayout和LinearLayout是Android中常用的佈局,兩者的使用會極大的影響程式生成每一幀的效能,因此,正確的使用它們是提升程式效能的重要工作。記得以前,較低的SDK版本新建Android專案時,預設的佈局檔案是採用線性佈局LinearLayout,但現在自動生成的佈局檔案都是RelativeLayout,或許你會認為這是IDE的預設設定問題,其實不然,這由 android-sdk\tools\templates\activities\BlankActivity\root\res\layout\activity_simple.xml.ftl 這個檔案事先就定好了的,也就是說這是Google的選擇,而非IDE的選擇。那SDK為什麼會預設給開發者新建一個預設的RelativeLayout佈局呢?<-----原因見最後小結 當然是因為RelativeLayout的效能更優,效能至上嘛。但是我們再看看預設新建的這個RelativeLayout的父容器,也就是當前視窗的頂級View——DecorView,它卻是個垂直方向的LinearLayout,上面是標題欄,下面是內容欄。那麼問題來了,Google為什麼給開發者預設新建了個RelativeLayout,而自己卻偷偷用了個LinearLayout,到底誰的效能更高,開發者該怎麼選擇呢? 下面將通過分析它們的原始碼來探討其View繪製效能,並得出其正確的使用方法。
一、View的一些基本工作原理
先通過幾個問題,簡單的瞭解寫android中View的工作原理吧。 (1)View是什麼? 簡單來說,View是Android系統在螢幕上的視覺呈現,也就是說你在手機螢幕上看到的東西都是View。 (2)View是怎麼繪製出來的? View的繪製流程是從ViewRoot的performTraversals()方法開始,依次經過measure(),layout()和draw()三個過程才最終將一個View繪製出來。 (3)View是怎麼呈現在介面上的? Android中的檢視都是通過Window來呈現的,不管Activity、Dialog還是Toast它們都有一個Window,然後通過WindowManager來管理View。Window和頂級View——DecorView的通訊是依賴ViewRoot完成的。 (4)View和ViewGroup什麼區別?不管簡單的Button和TextView還是複雜的RelativeLayout和ListView,他們的共同基類都是View。所以說,View是一種介面層控制元件的抽象,他代表了一個控制元件。那ViewGroup是什麼東西,它可以被翻譯成控制元件組,即一組View。ViewGroup也是繼承View,這就意味著View本身可以是單個控制元件,也可以是多個控制元件組成的控制元件組。根據這個理論,Button顯然是個View,而RelativeLayout不但是一個View還可以是一個ViewGroup,而ViewGroup內部是可以有子View的,這個子View同樣也可能是ViewGroup,以此類推。
二、RelativeLayout和LinearLayout效能PK
基於以上原理和大背景,我們要探討的效能問題,說的簡單明瞭一點就是:當RelativeLayout和LinearLayout分別作為ViewGroup,表達相同佈局時繪製在螢幕上時誰更快一點。上面已經簡單說了View的繪製,從ViewRoot的performTraversals()方法開始依次呼叫perfromMeasure、performLayout和performDraw這三個方法。這三個方法分別完成頂級View的measure、layout和draw三大流程,其中perfromMeasure會呼叫measure,measure又會呼叫onMeasure,在onMeasure方法中則會對所有子元素進行measure,這個時候measure流程就從父容器傳遞到子元素中了,這樣就完成了一次measure過程,接著子元素會重複父容器的measure,如此反覆就完成了整個View樹的遍歷。同理,performLayout和performDraw也分別完成perfromMeasure類似的流程。通過這三大流程,分別遍歷整棵View樹,就實現了Measure,Layout,Draw這一過程,View就繪製出來了。那麼我們就分別來追蹤下RelativeLayout和LinearLayout這三大流程的執行耗時。
如下圖,我們分別用兩用種方式簡單的實現佈局測試下
LinearLayout Measure:0.738ms Layout:0.176ms draw:7.655ms RelativeLayout Measure:2.280ms Layout:0.153ms draw:7.696ms 從這個資料來看無論使用RelativeLayout還是LinearLayout,layout和draw的過程兩者相差無幾,考慮到誤差的問題,幾乎可以認為兩者不分伯仲,關鍵是Measure的過程RelativeLayout卻比LinearLayout慢了一大截。 (1)RelativeLayout的onMeasure()方法
- protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mDirtyHierarchy) {
- mDirtyHierarchy = false;
- sortChildren();
- }
- int myWidth = -1;
- int myHeight = -1;
- int width = 0;
- int height = 0;
- finalint widthMode = MeasureSpec.getMode(widthMeasureSpec);
- finalint heightMode = MeasureSpec.getMode(heightMeasureSpec);
- finalint widthSize = MeasureSpec.getSize(widthMeasureSpec);
- finalint heightSize = MeasureSpec.getSize(heightMeasureSpec);
- // Record our dimensions if they are known;
- if (widthMode != MeasureSpec.UNSPECIFIED) {
- myWidth = widthSize;
- }
- if (heightMode != MeasureSpec.UNSPECIFIED) {
- myHeight = heightSize;
- }
- if (widthMode == MeasureSpec.EXACTLY) {
- width = myWidth;
- }
- if (heightMode == MeasureSpec.EXACTLY) {
- height = myHeight;
- }
- mHasBaselineAlignedChild = false;
- View ignore = null;
- int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
- finalboolean horizontalGravity = gravity != Gravity.START && gravity != 0;
- gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
- finalboolean verticalGravity = gravity != Gravity.TOP && gravity != 0;
- int left = Integer.MAX_VALUE;
- int top = Integer.MAX_VALUE;
- int right = Integer.MIN_VALUE;
- int bottom = Integer.MIN_VALUE;
- boolean offsetHorizontalAxis = false;
- boolean offsetVerticalAxis = false;
- if ((horizontalGravity || verticalGravity) && mIgnoreGravity != View.NO_ID) {
- ignore = findViewById(mIgnoreGravity);
- }
- finalboolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
- finalboolean isWrapContentHeight = heightMode != MeasureSpec.EXACTLY;
- // We need to know our size for doing the correct computation of children positioning in RTL
- // mode but there is no practical way to get it instead of running the code below.
- // So, instead of running the code twice, we just set the width to a "default display width"
- // before the computation and then, as a last pass, we will update their real position with
- // an offset equals to "DEFAULT_WIDTH - width".
- finalint layoutDirection = getLayoutDirection();
- if (isLayoutRtl() && myWidth == -1) {
- myWidth = DEFAULT_WIDTH;
- }
- View[] views = mSortedHorizontalChildren;
- int count = views.length;
- for (int i = 0; i < count; i++) {
- View child = views[i];
- if (child.getVisibility() != GONE) {
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- int[] rules = params.getRules(layoutDirection);
- applyHorizontalSizeRules(params, myWidth, rules);
- measureChildHorizontal(child, params, myWidth, myHeight);
- if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
- offsetHorizontalAxis = true;
- }
- }
- }
- views = mSortedVerticalChildren;
- count = views.length;
- finalint targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
- for (int i = 0; i < count; i++) {
- View child = views[i];
- if (child.getVisibility() != GONE) {
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- applyVerticalSizeRules(params, myHeight);
- measureChild(child, params, myWidth, myHeight);
- if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
- offsetVerticalAxis = true;
- }
- if (isWrapContentWidth) {
- if (isLayoutRtl()) {
- if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
- width = Math.max(width, myWidth - params.mLeft);
- } else {
- width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
- }
- } else {
- if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
- width = Math.max(width, params.mRight);
- } else {
- width = Math.max(width, params.mRight + params.rightMargin);
- }
- }
- }
- if (isWrapContentHeight) {
- if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
- height = Math.max(height, params.mBottom);
- } else {
- height = Math.max(height, params.mBottom + params.bottomMargin);
- }
- }
- if (child != ignore || verticalGravity) {
- left = Math.min(left, params.mLeft - params.leftMargin);
- top = Math.min(top, params.mTop - params.topMargin);
- }
- if (child != ignore || horizontalGravity) {
- right = Math.max(right, params.mRight + params.rightMargin);
- bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
- }
- }
- }
- if (mHasBaselineAlignedChild) {
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- alignBaseline(child, params);
- if (child != ignore || verticalGravity) {
- left = Math.min(left, params.mLeft - params.leftMargin);
- top = Math.min(top, params.mTop - params.topMargin);
- }
- if (child != ignore || horizontalGravity) {
- right = Math.max(right, params.mRight + params.rightMargin);
- bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
- }
- }
- }
- }
- if (isWrapContentWidth) {
- // Width already has left padding in it since it was calculated by looking at
- // the right of each child view
- width += mPaddingRight;
- if (mLayoutParams != null && mLayoutParams.width >= 0) {
- width = Math.max(width, mLayoutParams.width);
- }
- width = Math.max(width, getSuggestedMinimumWidth());
- width = resolveSize(width, widthMeasureSpec);
- if (offsetHorizontalAxis) {
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- finalint[] rules = params.getRules(layoutDirection);
- if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
- centerHorizontal(child, params, width);
- } elseif (rules[ALIGN_PARENT_RIGHT] != 0) {
- finalint childWidth = child.getMeasuredWidth();
- params.mLeft = width - mPaddingRight - childWidth;
- params.mRight = params.mLeft + childWidth;
- }
- }
- }
- }
- }
- if (isWrapContentHeight) {
- // Height already has top padding in it since it was calculated by looking at
- // the bottom of each child view
- height += mPaddingBottom;
- if (mLayoutParams != null && mLayoutParams.height >= 0) {
- height = Math.max(height, mLayoutParams.height);
- }
- height = Math.max(height, getSuggestedMinimumHeight());
- height = resolveSize(height, heightMeasureSpec);
- if (offsetVerticalAxis) {
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- finalint[] rules = params.getRules(layoutDirection);
- if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
- centerVertical(child, params, height);
- } elseif (rules[ALIGN_PARENT_BOTTOM] != 0) {
- finalint childHeight = child.getMeasuredHeight();
- params.mTop = height - mPaddingBottom - childHeight;
- params.mBottom = params.mTop + childHeight;
- }
- }
- }
- }
- }
- if (horizontalGravity || verticalGravity) {
- final Rect selfBounds = mSelfBounds;
- selfBounds.set(mPaddingLeft, mPaddingTop, width - mPaddingRight,
- height - mPaddingBottom);
- final Rect contentBounds = mContentBounds;
- Gravity.apply(mGravity, right - left, bottom - top, selfBounds, contentBounds,
- layoutDirection);
- finalint horizontalOffset = contentBounds.left - left;
- finalint verticalOffset = contentBounds.top - top;
- if (horizontalOffset != 0 || verticalOffset != 0) {
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getVisibility() != GONE && child != ignore) {
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- if (horizontalGravity) {
- params.mLeft += horizontalOffset;
- params.mRight += horizontalOffset;
- }
- if (verticalGravity) {
- params.mTop += verticalOffset;
- params.mBottom += verticalOffset;
- }
- }
- }
- }
- }
- if (isLayoutRtl()) {
- finalint offsetWidth = myWidth - width;
- for (int i = 0; i < count; i++) {
- View child = getChildAt(i);
- if (child.getVisibility() != GONE) {
- LayoutParams params = (LayoutParams) child.getLayoutParams();
- params.mLeft -= offsetWidth;
- params.mRight -= offsetWidth;
- }
- }
- }
- setMeasuredDimension(width, height);
- }
根據上述關鍵程式碼,RelativeLayout分別對所有子View進行兩次measure,橫向縱向分別進行一次,這是為什麼呢?首先RelativeLayout中子View的排列方式是基於彼此的依賴關係,而這個依賴關係可能和佈局中View的順序並不相同,在確定每個子View的位置的時候,需要先給所有的子View排序一下。又因為RelativeLayout允許A,B 2個子View,橫向上B依賴A,縱向上A依賴B。所以需要橫向縱向分別進行一次排序測量。 mSortedHorizontalChildren和mSortedVerticalChildren是分別對水平方向的子控制元件和垂直方向的子控制元件進行排序後的View陣列。
(2)LinearLayout的onMeasure()方法
- @Override
- protectedvoid onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- if (mOrientation == VERTICAL) {
- measureVertical(widthMeasureSpec, heightMeasureSpec);
- } else {
- measureHorizontal(widthMeasureSpec, heightMeasureSpec);
- }
- }
與RelativeLayout相比LinearLayout的measure就簡單的多,只需判斷線性佈局是水平佈局還是垂直佈局即可,然後才進行測量:
- /**
- * Measures the children when the orientation of this LinearLayout is set
- * to {@link #VERTICAL}.
- *
- * @param widthMeasureSpec Horizontal space requirements as imposed by the parent.
- * @param heightMeasureSpec Vertical space requirements as imposed by the parent.
- *
- * @see #getOrientation()
- * @see #setOrientation(int)
- * @see #onMeasure(int, int)
- */
- void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
- mTotalLength = 0;
- int maxWidth = 0;
- int childState = 0;
- int alternativeMaxWidth = 0;
- int weightedMaxWidth = 0;
- boolean allFillParent = true;
- float totalWeight = 0;
- finalint count = getVirtualChildCount();
- finalint widthMode = MeasureSpec.getMode(widthMeasureSpec);
- finalint heightMode = MeasureSpec.getMode(heightMeasureSpec);
- boolean matchWidth = false;
- boolean skippedMeasure = false;
- finalint baselineChildIndex = mBaselineAlignedChildIndex;
- finalboolean useLargestChild = mUseLargestChild;
- int largestChildHeight = Integer.MIN_VALUE;
- // See how tall everyone is. Also remember max width.
- for (int i = 0; i < count; ++i) {
- final View child = getVirtualChildAt(i);
- if (child == null) {
- mTotalLength += measureNullChild(i);
- continue;
- }
- if (child.getVisibility() == View.GONE) {
- i += getChildrenSkipCount(child, i);
- continue;
- }
- if (hasDividerBeforeChildAt(i)) {
- mTotalLength += mDividerHeight;
- }
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();
- totalWeight += lp.weight;
- if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
- // Optimization: don't bother measuring children who are going to use
- // leftover space. These views will get measured again down below if
- // there is any leftover space.
- finalint totalLength = mTotalLength;
- mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
- skippedMeasure = true;
- } else {
- int oldHeight = Integer.MIN_VALUE;
- if (lp.height == 0 && lp.weight > 0) {
- // heightMode is either UNSPECIFIED or AT_MOST, and this
- // child wanted to stretch to fill available space.
- // Translate that to WRAP_CONTENT so that it does not end up
- // with a height of 0
- oldHeight = 0;
- lp.height = LayoutParams.WRAP_CONTENT;
- }
- // 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).
- measureChildBeforeLayout(
- child, i, widthMeasureSpec, 0, heightMeasureSpec,
- totalWeight == 0 ? mTotalLength : 0);
- if (oldHeight != Integer.MIN_VALUE) {
- lp.height = oldHeight;
- }
- finalint childHeight = child.getMeasuredHeight();
- finalint totalLength = mTotalLength;
- mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
- lp.bottomMargin + getNextLocationOffset(child));
- if (useLargestChild) {
- largestChildHeight = Math.max(childHeight, largestChildHeight);
- }
- }
- /**
- * If applicable, compute the additional offset to the child's baseline
- * we'll need later when asked {@link #getBaseline}.
- */
- if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
- mBaselineChildTop = mTotalLength;
- }
- // if we are trying to use a child index for our baseline, the above
- // book keeping only works if there are no children above it with
- // weight. fail fast to aid the developer.
- if (i < baselineChildIndex && lp.weight > 0) {
- thrownew RuntimeException("A child of LinearLayout with index "
- + "less than mBaselineAlignedChildIndex has weight > 0, which "
- + "won't work. Either remove the weight, or don't set "
- + "mBaselineAlignedChildIndex.");
- }
- boolean matchWidthLocally = false;
- if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
- // The width of the linear layout will scale, and at least one
- // child said it wanted to match our width. Set a flag
- // indicating that we need to remeasure at least that view when
- // we know our width.
- matchWidth = true;
- matchWidthLocally = true;
- }
- finalint margin = lp.leftMargin + lp.rightMargin;
- finalint measuredWidth = child.getMeasuredWidth() + margin;
- maxWidth = Math.max(maxWidth, measuredWidth);
- childState = combineMeasuredStates(childState, child.getMeasuredState());
- allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
- if (lp.weight > 0) {
- /*
- * Widths of weighted Views are bogus if we end up
- * remeasuring, so keep them separate.
- */
- weightedMaxWidth = Math.max(weightedMaxWidth,
- matchWidthLocally ? margin : measuredWidth);
- } else {
- alternativeMaxWidth = Math.max(alternativeMaxWidth,
- matchWidthLocally ? margin : measuredWidth);
- }
- i += getChildrenSkipCount(child, i);
- }
- if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
- mTotalLength += mDividerHeight;
- }
- if (useLargestChild &&
- (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
- 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
- finalint totalLength = mTotalLength;
- mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
- lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
- }
- }
- // Add in our padding
- mTotalLength += mPaddingTop + mPaddingBottom;
- int heightSize = mTotalLength;
- // Check against our minimum height
- heightSize = Math.max(heightSize, getSuggestedMinimumHeight());
- // Reconcile our calculated size with the heightMeasureSpec
- int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
- heightSize = heightSizeAndState & MEASURED_SIZE_MASK;
- // Either expand children with weight to take up available space or
- // shrink them if they extend beyond our current bounds. If we skipped
- // measurement on any children, we need to measure them now.
- int delta = heightSize - mTotalLength;
- if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
- 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
- int share = (int) (childExtra * delta / weightSum);
- weightSum -= childExtra;
- delta -= share;
- finalint 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
- 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));
- }
- // Child may now not fit in vertical dimension.
- childState = combineMeasuredStates(childState, child.getMeasuredState()
- & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
- }
- finalint margin = lp.leftMargin + lp.rightMargin;
- finalint 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;
- finalint 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);
- // We have no limit, so make all weighted views as tall as the largest child.
- // Children will have already been measured once.
- if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
- for (int i = 0; i < count; i++) {
- final View child = getVirtualChildAt(i);
- if (child == null || child.getVisibility() == View.GONE) {
- continue;
- }
- final LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) child.getLayoutParams();
- float childExtra = lp.weight;
- if (childExtra > 0) {
- child.measure(
- MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
- MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(largestChildHeight,
- MeasureSpec.EXACTLY));
- }
- }
- }
- }
- if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
- maxWidth = alternativeMaxWidth;
- }
- maxWidth += mPaddingLeft + mPaddingRight;
- // Check against our minimum width
- maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
- setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
- heightSizeAndState);
- if (matchWidth) {
- forceUniformWidth(count, heightMeasureSpec);
- }
- }
LinearLayout首先會對所有的子View進行measure,並計算totalWeight(所有子View的weight屬性之和),然後判斷子View的weight屬性是否為最大,如為最大則將剩餘的空間分配給它。如果不使用weight屬性進行佈局,則不進行第二次measure。
父檢視在對子檢視進行measure操作的過程中,使用變數mTotalLength儲存已經measure過的child所佔用