Android RelativeLayout和LinearLayout

我们知道Android中页面布局有以下五种:

五大布局简介

  • LinearLayout 线性布局

线性布局是按照水平或垂直的顺序将子元素(可以是控件或布局)依次按照顺序排列,每一个元素都位于前面一个元素之后。线性布局分为两种:水平方向和垂直方向的布局。分别通过属性android:orientation="vertical" 和 android:orientation="horizontal"来设置。android:layout_weight 表示子元素占据的空间大小的比例,有人说这个值大小和占据空间成正比,有人说反比。(稍后会研究一下

  • RelativeLayout 相对布局

RelativeLayout继承于android.widget.ViewGroup,其按照子元素之间的位置关系完成布局的,作为Android系统五大布局中最灵活也是最常用的一种布局方式,非常适合于一些比较复杂的界面设计。

注意:在引用其他子元素之前,引用的ID必须已经存在,否则将出现异常。

  • TableLayout 表格布局

表格布局,适用于多行多列的布局格式,每个TableLayout是由多个TableRow组成,一个TableRow就表示TableLayout中的每一行,这一行可以由多个子元素组成。实际上TableLayout和TableRow都是LineLayout线性布局的子类。但是TableRow的参数android:orientation属性值固定为horizontal,且android:layout_width=MATCH_PARENT android:layout_height=WRAP_CONTENT。所以TableRow实际是一个横向的线性布局,且所以子元素宽度和高度一致。

注意:在TableLayout中,单元格可以为空,但是不能跨列,意思是只能不能有相邻的单元格为空。

在TableLayout布局中,一列的宽度由该列中最宽的那个单元格指定,而该表格的宽度由父容器指定。可以为每一列设置以下属性:

 Shrinkable  表示该列的宽度可以进行收缩,以使表格能够适应父容器的大小

 Stretchable 表示该列的宽度可以进行拉伸,以使能够填满表格中的空闲空间

 Collapsed  表示该列会被隐藏

TableLayout中的特有属性:

android:collapseColumns

android:shrinkColumns

android:stretchColumns = "0,1,2,3"

  • AbsoluteLayou 绝对布局
绝对布局中将所有的子元素通过设置android:layout_x 和 android:layout_y属性,将子元素的坐标位置固定下来,即坐标(android:layout_x, android:layout_y) ,layout_x用来表示横坐标,layout_y用来表示纵坐标。 屏幕左上角为坐标(0,0),横向往右为正方,纵向往下为正方。实际应用中,这种布局用的比较少,因为Android终端一般机型比较多,各自的屏幕大小。分辨率等可能都不一样,如果用绝对布局,可能导致在有的终端上显示不全等。
  • FrameLayout 框架布局
将所有的子元素放在整个界面的左上角,后面的子元素直接覆盖前面的子元素,所以用的比较少。


我们经常使用的是前两种,效果图就不用贴了,私下自己搞搞就明白,那么这两种有什么区别呢?这里有个性能优化的文章可以参考以下:性能优化典范

两者相似之处:

控件宽度
android:layout_width="80px" //"80dip"或"80dp"
android:layout_width =“wrap_content”
android:layout_width =“match_parent” 

控件高度
android:layout_height="80px" //"80dip"或"80dp"
android:layout_height =“wrap_content”
android:layout_height =“match_parent” 

控件排布
android:orientation="horizontal”
android:orientation="vertical“

控件间距
android:layout_marginLeft="5dip" //距离左边
android:layout_marginRight="5dip" //距离右边
android:layout_marginTop="5dip" //距离上面
android:layout_marginRight="5dip" //距离下面

android:paddingLeft="5dip"


控件显示位置
android:gravity="center" //left,right, top, bottom
android:gravity="center_horizontal"

android:layout_gravity是本元素对父元素的重力方向。
android:layout_gravity属性则设置控件本身相对于父控件的显示位置
android:gravity是本元素所有子元素的重力方向。

android:layout_gravity="center_vertical"
android:layout_gravity="left"
android:layout_gravity="left|bottom"


TextView中文本字体
android:text="@String/text1" //在string.xml中定义text1的值
android:textSize="20sp"
android:textColor=”#ff123456”
android:textStyle="bold" //普通(normal), 斜体(italic),粗斜体(bold_italic)

TextView中,控制其以...结束

android:ellipsize="end"

只有一行

android:singleLine="true"

定义控件是否可见
android:visibility=”visible” //可见
android:visibility=”invisible”  //不可见,但是在布局中占用的位置还在
android:visibility=”gone”   //不可见,完全从布局中消失

定义背景图片
android:background="@drawable/img_bg" //img_bg为drawable下的一张图片

seekbar控件背景图片及最大值
android:progressDrawable="@drawable/seekbar_img" 
android:thumb="@drawable/thumb"     
android:max = "60"

android:layout_alignWithParentIfMissing="true"

不同之处

仅在RelativeLayout中有效
在父亲布局的相对位置
android:layout_alignParentLeft="true" //在布局左边
android:layout_alignParentRight="true" //在布局右边
android:layout_alignParentTop="true" //在布局上面
android:layout_alignParentBottom="true " //在布局的下面

在某个控件的相对位置
android:layout_toRightOf="@id/button1" //在控件button1的右边,不仅仅是紧靠着
android:layout_toLeftOf="@id/button1" //在控件button2的左边,不仅仅是紧靠着
android:layout_below="@id/button1 " //在控件button1下面,不仅仅是正下方
android:layout_above=“@id/button1” //在控件button1下面,不仅仅是正下方

定义和某控件对奇
android:layout_alignTop=”@id/button1” //和控件button1上对齐
android:layout_alignBottom=”@id/button1” //和控件button1下对齐
android:layout_alignLeft=”@id/button1” //和控件button1左对齐
android:layout_alignRight=”@id/button1” //和控件button2右对齐


android:layout_centerHorizontal="true"   //水平居中
android:layout_centerVertical="true"
android:layout_centerInParent="true" 

仅在LinearLayout中有效
设置控件在一排或一列中所占比例值
android:layout_weight="1"

性能分析

他们的性能差异主要在onMeasure()上:
RelativeLayout
@Override
protected 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++) {
    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);
        }
    }
}
......
}


根据上述关键代码,RelativeLayout分别对所有子View进行两次measure,横向纵向分别进行一次。

LinearLayout

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
......
for (int i = 0; i < count; ++i) {
    ......

    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);
        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;
        }

        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);
        }
    }
    ......

LinearLayout首先会对所有的子View进行measure,并计算totalWeight(所有子View的weight属性之和),然后判断子View的weight属性是否为最大,如为最大则将剩余的空间分配给它。如果不使用weight属性进行布局,则不进行第二次measure。
 
 
<span style="font-family:SimSun;font-size:12px;">// 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;

            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
                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));
        }

        ......
    }
     ......
} 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));
            }
        }
    }
}
......
}</span>
结论:RelativeLayout将对所有的子View进行两次measure,而LinearLayout在使用weight属性进行布局时也会对子View进行两次measure,如果他们位于整个View树的顶端时并可能进行多层的嵌套时,位于底层的View将会进行大量的measure操作,大大降低程序性能。因此,应尽量将RelativeLayout和LinearLayout置于View树的底层,并减少嵌套。

文章参考:http://www.tuicool.com/articles/uQ3MBnj

【欢迎上码】

【微信公众号搜索 h2o2s2】


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值