@universal
2018-05-15T06:50:33.000000Z
字数 4130
阅读 446
view
relativelayout和linerlayout都是继承自ViewGroup,所以要比较view的性能还是要看onMeasure、onLayout、onDraw三个方法。有测试数据表示在加载相同布局情况下,linerlayout的onLayout、onDraw方法的执行时间和relativelayout差不多,但是liner的onMeasure()时间要短很多。既然出现了差异,那么就从源码角度具体分析。
首先来看RelativeLayout的OnMeasure()
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {......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);}}}......}
上面代码只截取了核心的一部分,可以看到relativelayout对子view进行了两次measure,因为相对布局的子view是基于相对位置分布的,子view之间可能分别存在横向和纵向上依赖关系,所以需要在横向和纵向分别进行一次measure来确定view的位置,最后都会调用child.measure();
再来看LinerLayout的OnMeasure:
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mOrientation == VERTICAL) {measureVertical(widthMeasureSpec, heightMeasureSpec);} else {measureHorizontal(widthMeasureSpec, heightMeasureSpec);}}
可以看出来linerlayout只针对布局方向进行了一次measure。
具体的看下measureVertical()方法
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {.....for (int i = 0; i < count; ++i) {final View child = getVirtualChildAt(i);.....final LayoutParams lp = (LayoutParams) child.getLayoutParams();totalWeight += lp.weight;final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;//设置了weight值if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {final int totalLength = mTotalLength;mTotalLength = Math.max(totalLength, totalLength + lp.topMargin+lp.bottomMargin);skippedMeasure = true;//标记位,跳过measure}else {....measureChildBeforeLayout(child,i,widthMeasureSpec,0,heightMeasureSpec,usedHeight);....}}.....if (skippedMeasure || remainingExcess != 0 && totalWeight > 0.0f) {for (int i = 0; i < count; ++i) {final View child = getVirtualChildAt(i);final LayoutParams lp = (LayoutParams) child.getLayoutParams();final float childWeight = lp.weight;if (childWeight > 0) {......final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(0, childHeight), MeasureSpec.EXACTLY);final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin,lp.width);//针对weight大于0的viewchild.measure(childWidthMeasureSpec, childHeightMeasureSpec);}}}}
上面只截取了部分核心代码,可以看到如果子view的layout:weight>0且layout:height=0的时候,第一次measure跳过这些view,并设置标记位skippedMeasure为true,在后面对这些view进行第二次measure。同理,measureHorizontal()里的逻辑类似。
总结:相同的布局情况下,relativet会对子View进行两次measure,liner只对子View进行一次measure,而在设置了weight时,也会对weight进行两次measure,所以一般情况下LinearLayout的性能要优于RelativeLayout。
如果view的层级嵌套过多,尽量使用relativelayout来降低层级,因为relative比较灵活而且层级结构比较扁平,很多liner的嵌套都可以用一个relative来代替。这也是Google推荐的方式(之前新建项目的布局都是relativelayout)。