在写布局文件中 RelativeLayout 和 LinearLayout 用的是较多的两种布局;而这两种哪一种方式性能更高?
1.首先了解 View 的绘制流程。
不管简单的 TextView 还是复杂的 RelativeLayout ,他们的共同基类都是 View ;所以它们都有三个重要的绘制方法(不详细介绍):Measure(测量),Layout(布局) 和 Draw(绘制);
2.对比 RelativeLayout 和 LinearLayout 性能。
View 的绘制流程会走 measure(),layout() 和 draw() 这三个重要的方法,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 慢了很多。下面是针对 RelativeLayout 和 LinearLayout 中的 Measure 方法分析和对比。
RelativeLayout 的 onMeasure() 方法(代码太多,截取了一部分):
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//此处省略...
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;
}
//此处省略...
}
}
//此处省略...
setMeasuredDimension(width, height);
}
根据源码发现 RelativeLayout 会对子 View 做两次 measure。因为 RelativeLayout 中子 View 的排列方式是基于彼此的依赖关系,而这个依赖关系可能和布局中 View 的顺序并不相同,在确定每个子 View 的位置的时候,就需要先给所有的子 View 排序一下。所以需要横向纵向分别测量。
LinearLayout的 onMeasure() 方法(代码太多,截取了一部分):
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}
从上面的代码可以看出, LinearLayout 先进行判断为横向还是纵向,然后再进行一次测量。
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);
}
}
3.RelativeLayout 和 LinearLayout 性能总结。
总结: RelativeLayout会让子View调用2次onMeasure,而 LinearLayout 在没有 weight 时,只会调用1次onMeasure(如果 LinearLayout 有 weight 属性时,也会调用两次 onMeasure );因此LinearLayout 性能要高于 RelativeLayout 的性能,在实际操作时可以用 LinearLayout 完成,尽量用 LinearLayout 不去用 RelativeLayout 。