轉載請注明出處:http://blog.csdn.net/hejjunlin/article/details/51159419
怎樣優化你的布局層級結構之RelativeLayout和LinearLayout及FrameLayout性能分析
工作一段時間后。常常會被領導說,你這個進入速度太慢了,競品的進入速度非常快,你搞下優化吧?每當這時,你會怎么辦?功能實現都有啊。進入時要載入那么多view。這也沒辦法啊。等等。
先看一些現象吧:用Android studio,新建一個Activity自己主動生成的布局文件都是RelativeLayout,也許你會覺得這是IDE的默認設置問題。事實上不然,這是由 android-sdk\tools\templates\activities\EmptyActivity\root\res\layout\activity_simple.xml.ftl 這個文件事先就定好了的,也就是說這是Google的選擇。而非IDE的選擇。
那SDK為什么會默認給開發人員新建一個默認的RelativeLayout布局呢?當然是由於RelativeLayout的性能更優。性能至上嘛。可是我們再看看默認新建的這個RelativeLayout的父容器,也就是當前窗體的頂級View——DecorView,它卻是個垂直方向的LinearLayout,上面是標題欄,以下是內容欄。那么問題來了。Google為什么給開發人員默認新建了個RelativeLayout,而自己卻偷偷用了個LinearLayout。究竟誰的性能更高,開發人員該怎么選擇呢?
View的一些基本工作原理
先通過幾個問題,簡單的了解寫android中View的工作原理吧。
View是什么?
簡單來說,View是Android系統在屏幕上的視覺呈現,也就是說你在手機屏幕上看到的東西都是View。
View是怎么繪制出來的?
View的繪制流程是從ViewRoot的performTraversals()方法開始,依次經過measure(),layout()和draw()三個過程才終於將一個View繪制出來。
View是怎么呈如今界面上的?
Android中的視圖都是通過Window來呈現的。無論Activity、Dialog還是Toast它們都有一個Window,然后通過WindowManager來管理View。Window和頂級View——DecorView的通信是依賴ViewRoot完畢的。
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.762ms
Layout:0.167ms
draw:7.665ms
RelativeLayout
Measure:2.180ms
Layout:0.156ms
draw:7.694ms
從這個數據來看無論使用RelativeLayout還是LinearLayout,layout和draw的過程兩者相差無幾。考慮到誤差的問題。差點兒能夠覺得兩者不分伯仲,關鍵是Measure的過程RelativeLayout卻比LinearLayout慢了一大截。
Measure都干什么了
RelativeLayout的onMeasure()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mDirtyHierarchy) {
mDirtyHierarchy = false;
sortChildren();
}
int myWidth = -1;
int myHeight = -1;
int width = 0;
int height = 0;
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
final int 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;
}
View ignore = null;
int gravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
final boolean horizontalGravity = gravity != Gravity.START && gravity != 0;
gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
final boolean 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);
}
final boolean isWrapContentWidth = widthMode != MeasureSpec.EXACTLY;
final boolean 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".
final int 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;
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
applyVerticalSizeRules(params, myHeight, child.getBaseline());
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);
}
}
}
// Use the top-start-most laid out view as the baseline. RTL offsets are
// applied later, so we can use the left-most edge as the starting edge.
View baselineView = null;
LayoutParams baselineParams = null;
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final LayoutParams childParams = (LayoutParams) child.getLayoutParams();
if (baselineView == null || baselineParams == null
|| compareLayoutPosition(childParams, baselineParams) < 0) {
baselineView = child;
baselineParams = childParams;
}
}
}
mBaselineView = baselineView;
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++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
final int[] rules = params.getRules(layoutDirection);
if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) {
centerHorizontal(child, params, width);
} else if (rules[ALIGN_PARENT_RIGHT] != 0) {
final int 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++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
final int[] rules = params.getRules(layoutDirection);
if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) {
centerVertical(child, params, height);
} else if (rules[ALIGN_PARENT_BOTTOM] != 0) {
final int 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);
final int horizontalOffset = contentBounds.left - left;
final int verticalOffset = contentBounds.top - top;
if (horizontalOffset != 0 || verticalOffset != 0) {
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE && child != ignore) {
final LayoutParams params = (LayoutParams) child.getLayoutParams();
if (horizontalGravity) {
params.mLeft += horizontalOffset;
params.mRight += horizontalOffset;
}
if (verticalGravity) {
params.mTop += verticalOffset;
params.mBottom += verticalOffset;
}
}
}
}
}
if (isLayoutRtl()) {
final int offsetWidth = myWidth - width;
for (int i = 0; i < count; i++) {
final View child = views[i];
if (child.getVisibility() != GONE) {
final 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。所以須要橫向縱向分別進行一次排序測量。
LinearLayout的onMeasure()方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
與RelativeLayout相比LinearLayout的measure就簡單明了的多了,先推斷線性規則,然后運行相應方向上的測量。隨便看一個吧。
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.
final int totalLength = mTotalLength;
mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
} 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;
}
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);
}
}
父視圖在對子視圖進行measure操作的過程中,使用變量mTotalLength保存已經measure過的child所占用的高度,該變量剛開始時是0。在for循環中調用measureChildBeforeLayout()對每個child進行測量,該函數實際上僅僅是調用了measureChildWithMargins(),在調用該方法時,使用了兩個參數。當中一個是heightMeasureSpec。該參數為LinearLayout本身的measureSpec;還有一個參數就是mTotalLength,代表該LinearLayout已經被其子視圖所占用的高度。 每次for循環對child測量完畢后,調用child.getMeasuredHeight()獲取該子視圖終於的高度,並將這個高度加入到mTotalLength中。在本步驟中。臨時避開了lp.weight>0的子視圖。即臨時先不測量這些子視圖,由於后面將把父視圖剩余的高度依照weight值的大小平均分配給相應的子視圖。源代碼中使用了一個局部變量totalWeight累計全部子視圖的weight值。
處理lp.weight>0的情況須要注意。假設變量heightMode是EXACTLY,那么,當其它子視圖占滿父視圖的高度后,weight>0的子視圖可能分配不到布局空間,從而不被顯示,僅僅有當heightMode是AT_MOST或者UNSPECIFIED時。weight>0的視圖才干優先獲得布局高度。
最后我們的結論是:假設不使用weight屬性。LinearLayout會在當前方向上進行一次measure的過程,假設使用weight屬性,LinearLayout會避開設置過weight屬性的view做第一次measure。完了再對設置過weight屬性的view做第二次measure。由此可見。weight屬性對性能是有影響的,並且本身有大坑,請注意避讓。
本文出自逆流的魚:http://blog.csdn.net/hejjunlin/article/details/51159419
小結
從源代碼中我們似乎能看出,我們先前的測試結果中RelativeLayout不如LinearLayout快的根本原因是RelativeLayout須要對其子View進行兩次measure過程。而LinearLayout則僅僅需一次measure過程,所以顯然會快於RelativeLayout,可是假設LinearLayout中有weight屬性。則也須要進行兩次measure,但即便如此,應該仍然會比RelativeLayout的情況好一點。
RelativeLayout還有一個性能問題
對照到這里就結束了嘛?顯然沒有!
我們再看看View的Measure()方法都干了些什么?
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
......
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}
可是上面已經說了RelativeLayout要做兩次measure,而在做橫向的測量時,縱向的測量結果尚未完畢。僅僅好臨時使用myHeight傳入子View系統。假如子View的Height不等於(設置了margin)myHeight的高度,那么measure中上面代碼所做得優化將不起作用。這一過程將進一步影響RelativeLayout的繪制性能。
而LinearLayout則無這方面的擔憂。解決問題也非常好辦,假設能夠,盡量使用padding取代margin。
FrameLayout和LinearLayout性能PK
LinearLayout
Measure:2.058ms
Layout:0.296ms
draw:3.857ms
FrameLayout
Measure:1.334ms
Layout:0.213ms
draw:3.680ms
從這個數據來使用LinearLayout,僅嵌套一個LinearLayou,在onMeasure就相關2倍時間和FrameLayout相比,layout和draw的過程兩者相差無幾,考慮到誤差的問題,差點兒能夠覺得兩者不分伯仲
看下FrameLayout的源代碼。做了什么?
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
//當FrameLayout的寬和高,僅僅有同一時候設置為match_parent或者指定的size,那么這個
//measureMatchParentChlidren = false,否則為true。以下會用到這個變量
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0; //寬高的期望類型
for (int i = 0; i < count; i++) { //一次遍歷每個不為GONE的子view
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//去掉FrameLayout的左右padding。子view的左右margin,這時候,再去
//計算子view的期望的值
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
/*maxWidth找到子View中最大的寬。高同理,為什么要找到他,由於在這里,FrameLayout是wrap
-content.他的寬高肯定受子view的影響*/
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());
/*以下的推斷,僅僅有上面的FragLayout的width和height都設置為match_parent 才不會運行
此處的mMatchParentChlidren的list里存的是設置為match_parent的子view。
結合上面兩句話的意思,當FrameLayout設置為wrap_content。這時候要把全部寬高設置為
match_parent的子View都記錄下來,記錄下來干什么呢?
這時候FrameLayout的寬高同一時候受子View的影響*/
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
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));
count = mMatchParentChildren.size();//這個大小就是子view中設定為match_parent的個數
if (count > 1) {
for (int i = 0; i < count; i++) {
//這里看上去又一次計算了一遍
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
int childWidthMeasureSpec;
int childHeightMeasureSpec;
/*假設子view的寬是match_parent,則寬度期望值是總寬度-padding-margin
假設子view的寬是指定的比方100dp。則寬度期望值是padding+margin+width
這個非常easy理解,以下的高同理*/
if (lp.width == LayoutParams.MATCH_PARENT) {
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -
getPaddingLeftWithForeground() - getPaddingRightWithForeground() -
lp.leftMargin - lp.rightMargin,
MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
if (lp.height == LayoutParams.MATCH_PARENT) {
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -
getPaddingTopWithForeground() - getPaddingBottomWithForeground() -
lp.topMargin - lp.bottomMargin,
MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
//把這部分子view又一次計算大小
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
加了一個嵌套。onMeasure時間,多了將近一倍,原因在於:LinearLayout在某一方向onMeasure。發現還存在LinearLayout。將觸發
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;
}
}
結論
1.RelativeLayout會讓子View調用2次onMeasure,LinearLayout 在有weight時。也會調用子View2次onMeasure
2.RelativeLayout的子View假設高度和RelativeLayout不同,則會引發效率問題,當子View非常復雜時,這個問題會更加嚴重。
假設能夠,盡量使用padding取代margin。
3.在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。
最后再思考一下文章開頭那個矛盾的問題,為什么Google給開發人員默認新建了個RelativeLayout,而自己卻在DecorView中用了個LinearLayout。由於DecorView的層級深度是已知並且固定的,上面一個標題欄,以下一個內容欄。採用RelativeLayout並不會減少層級深度,所以此時在根節點上用LinearLayout是效率最高的。而之所以給開發人員默認新建了個RelativeLayout是希望開發人員能採用盡量少的View層級來表達布局以實現性能最優,由於復雜的View嵌套對性能的影響會更大一些。
4.能用兩層LinearLayout,盡量用一個RelativeLayout,在時間上此時RelativeLayout耗時更小。另外LinearLayout慎用layout_weight,也將會添加一倍耗時操作。
由於使用LinearLayout的layout_weight,大多數時間是不一樣的,這會減少測量的速度。
這僅僅是一個怎樣合理使用Layout的案例,必要的時候。你要小心考慮是否用layout weight。總之減少層級結構,才是王道。讓onMeasure做延遲載入,用viewStub,include等一些技巧。
第一時間獲得博客更新提醒,以及很多其它android干貨,源代碼分析。歡迎關注我的微信公眾號,掃一掃下方二維碼,就可以關注。 
