Android RelativeLayout和LinearLayout

来源:互联网 发布:指南针炒股软件卸载 编辑:程序博客网 时间:2024/05/21 07:50

我们知道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

@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++) {    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

@Overrideprotected 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

0 0
原创粉丝点击