Android精讲--界面编程2(布局管理器)

为什么需要布局管理器


为了更好地管理Android应用的用户界面里的各种组件,Android提供了布局管理器。通过使用布局管理器,Android应用的图形用户界面具有良好的平台无关性。通常来说,推荐使用布局管理器来管理组件的分布、大小,而不是直接设置组件位置和大小。

android布局管理器类图

线性布局


LinearLayout,线性布局。LinearLayout将容器内的组件一个挨着一个地排列起来。LinearLayout可以控制各组件横向排列(通过设置android:orientation属性控制),也可控制各组件纵向排列。

# LinearLayout常用XML属性及相关方法

XML属性 相关方法 说明
android:baselineAligned setBaselineAligned(boolean) 该属性设为false,将会阻止该布局管理器与它的子元素的基线对齐
android:divider setDividerDrawable(Drawable) 设置垂直布局时两个按钮之间的分割线
android:gravity setGravity(int) 设置布局管理器内组件的对齐方式。属性有:top、bottom、left、right、center_vertical、fill_vertical、center_horizontal、fill_horizontal、center、fill、clip_vertical、clip_horizontal。也可以同时指定多种对齐方式的组合
android:measureWithLargestChild setMeasureWithLargestChildEnabled(boolean) 当该属性设为true时,所有带权重的子元素都会具有最大子元素的最小尺寸
android:orientation setOrientation(int) 设置布局管理器内组件的排列方式,可以设置为horizontal(水平排列)、vertical(垂直排列、默认值)

LinearLayout包含的所有子元素都受LinearLayout.LayoutParams控制,因此LinearLayout包含的子元素可以额外指定如下属性:

XML属性 说明
android:layout_gravity 指定该子元素在LinearLayout中的对齐方式
android:layout_weight 指定该子元素在LinearLayout中所占的权重

示例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="top"
    >
<Button  
    android:id="@+id/bn1"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="@string/bn1"
    />
<Button  
    android:id="@+id/bn2"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="@string/bn2"
    />
<Button  
    android:id="@+id/bn3"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="@string/bn3"
    />
<Button  
    android:id="@+id/bn4"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="@string/bn4"
    />
<Button  
    android:id="@+id/bn5"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="@string/bn5"
    />  
</LinearLayout>

表格布局


表格布局TableLayout继承了LinearLayout,因此它的本质依然是线性布局管理器。表格布局采用行、列的形式来管理UI组件,TableLayout并不需要明确地声明包含多少行、多少列,而是通过添加TableRow、其它组件来控制表格的行数和列数。

TableLayout中,列的宽度由该列中最宽的那个单元格决定,整个TableLayout的宽度则取决于父容器的宽度(默认总是占满父容器本身)。
TableLayout中,可以为单元格设置以下三种行为方式:

  • Shrinkable:如果某个列被设为Shrinkable,那么该列的所有单元格的宽度可以被收缩,以保证该表格能适应父容器的宽度。
  • Strechable:如果某个列被设为Strechable,那么该列的所有单元格的宽度可以被拉伸,以保证组件能完全填满表格空余空间。
  • Collapsed:如果某个列被设为Collapsed,那么该列的所有单元格会被隐藏。

TableLayout支持LinearLayout所有的XML属性,此外,还支持上面的3中属性:

XML属性 相关方法 说明
android:collapseColumns setColumnCollapsed(int,boolean) 设置需要被隐藏的列的列序号,多个列序号之间用逗号隔开
android:shrinkColumns setShrinkAllColumns(boolean) 设置允许被收缩的列的列序号,多个列序号之间用逗号隔开
android:strechColumns setStrechAllColumns(boolean) 设置允许被拉伸的列的列序号,多个列序号之间用逗号隔开

示例:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<!-- 定义第一个表格布局,指定第2列允许收缩,第3列允许拉伸 -->
<TableLayout android:id="@+id/TableLayout01" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:shrinkColumns="1"
    android:stretchColumns="2"
>
<!-- 直接添加按钮,它自己会占一行 -->
<Button android:id="@+id/ok1" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="独自一行的按钮"
    />
<!-- 添加一个表格行 -->
<TableRow>
<!-- 为该表格行添加3个按钮 -->
<Button android:id="@+id/ok2" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="普通按钮"
    />  
<Button android:id="@+id/ok3" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="收缩的按钮"
    /> 
<Button android:id="@+id/ok4" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="拉伸的按钮"
    />
</TableRow> 
</TableLayout>
<!-- 定义第二个表格布局 ,指定第二列隐藏-->
<TableLayout android:id="@+id/TableLayout01" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:collapseColumns="1"
>
<!-- 直接添加按钮,它自己会占一行 -->
<Button android:id="@+id/ok5" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text=" 独自一行的按钮 "
    />
<!--定义一个表格行-->
<TableRow>
<!-- 为该表格行添加3个按钮 -->
<Button android:id="@+id/ok6" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="普通按钮1"
    />  
<Button android:id="@+id/ok7" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="被隐藏的按钮"
    /> 
<Button android:id="@+id/ok8" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="普通按钮 3"
    />
</TableRow> 
</TableLayout>
<!-- 定义第三个表格布局 ,指定第2、3两列可以被拉伸-->
<TableLayout android:id="@+id/TableLayout01" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:stretchColumns="1,2"
>
<!-- 直接添加按钮,它自己会占一行 -->
<Button android:id="@+id/ok9" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="独自一行的按钮"
    />
<!--定义一个表格行-->
<TableRow>
<!-- 为该表格行添加3个按钮 -->
<Button android:id="@+id/ok10" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="普通按钮"
    />  
<Button android:id="@+id/ok11" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="拉伸的按钮"
    /> 
<Button android:id="@+id/ok12" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="拉伸的按钮"
    />
</TableRow> 
<!--定义一个表格行-->
<TableRow>
<!-- 为该表格行添加2个按钮 -->
<Button android:id="@+id/ok13" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="普通按钮"
    />  
<Button android:id="@+id/ok14" 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="拉伸的按钮"
    />
</TableRow> 
</TableLayout>
</LinearLayout>

帧布局


FramleLayout为每个加入其中的组件创建一个空白的区域(称为一帧),每个子组件占据一帧,这些帧都会根据gravity属性执行自动对齐。

# FrameLayout的常用XML属性及相关方法

XML属性 相关方法 说明
android:foreground setForeground(Drawable) 设置该帧布局容器的前景图像
android:foregroundGravity setForeground(int) 定义绘制前景图像的gravity属性

FrameLayout包含的子元素也受FrameLayout.LayoutParams控制,因此它所包含的子元素也可指定android:layout_gravity属性,该属性控制该子元素在FrameLayout中的对齐方式。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<!-- 依次定义6个TextView,先定义的TextView位于底层
    后定义的TextView位于上层 -->
<TextView android:id="@+id/view01"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:width="320px"
    android:height="320px"
    android:background="#f00"
    />
<TextView android:id="@+id/view02"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:width="280px"
    android:height="280px"
    android:background="#0f0"
    />
<TextView android:id="@+id/view03"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:width="240px"
    android:height="240px"
    android:background="#00f"
    />
<TextView android:id="@+id/view04"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:width="200px"
    android:height="200px"
    android:background="#ff0"
    />
<TextView android:id="@+id/view05"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:width="160px"
    android:height="160px"
    android:background="#f0f"
    />
<TextView android:id="@+id/view06"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:width="120px"
    android:height="120px"
    android:background="#0ff"
    />
</FrameLayout>

相对布局


RelativeLayout内子组件的位置总是相对兄弟组件、父容器来决定的,因此这种布局方式被称为相对布局。

如果A组件的位置是由B组件的位置来决定的,Android要求先定义B组件,再定义A组件。
# RelativeLayout的XML属性及相关方法说明

XML属性 相关方法 说明
android:gravity setGravity(int) 设置该布局容器内各子组件的对齐方式
android:ignoreGravity setIgnoreGravity(int) 设置哪个组件不受gravity属性的影响

为了控制该布局容器中各子组件的布局分布,RelativeLayout提供了一个内部类:RelativeLayout.LayoutParams,该类提供了大量的XML属性来控制RelativeLayout布局容器中子组件的布局分布。
RelativeLayout.LayoutParams里只能设为boolean值得属性

XML属性 说明
android:layout_centerHorizontal 控制该子组件是否位于布局容器的水平居中
android:layout_centerVertical 控制该子组件是否位于布局容器的垂直居中
android:layout_centerInParent 控制该子组件是否位于布局容器的中央位置
android:layout_alignParentBottom 控制该子组件是否与布局容器底部对齐
android:layout_centerParentLeft 控制该子组件是否位于布局容器左边对齐
android:layout_centerParentRight 控制该子组件是否位于布局容器右边对齐
android:layout_centerParentTop 控制该子组件是否位于布局容器顶端对齐

# RelativeLayout.LayoutParams里只能设为其它UI组件ID的属性

XML属性 说明
android:layout_toRightOf 控制该子组件位于给出ID组件的右侧
android:layout_toLeftOf 控制该子组件位于给出ID组件的左侧
android:layout_above 控制该子组件位于给出ID组件的上方
android:layout_below 控制该子组件位于给出ID组件的下方
android:layout_alignTop 控制该子组件位于给出ID组件的上边界对齐
android:layout_alignBottom 控制该子组件位于给出ID组件的下边界对齐
android:layout_alignLeft 控制该子组件位于给出ID组件的左边界对齐
android:layout_alignRight 控制该子组件位于给出ID组件的右边界对齐

此外,RelativeLayout.LayoutParams还继承了android.view.ViewGroup.MarginLayoutParams,因此RelativeLayout布局容器中每个子组件也可指定android.view.ViewGroup.MarginLayoutParams所支持的各XML属性。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<!-- 定义该组件位于父容器中间 -->   
<TextView 
    android:id="@+id/view01"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:background="@drawable/leaf"
    android:layout_centerInParent="true"
    />
<!-- 定义该组件位于view01组件的上方 -->
<TextView 
    android:id="@+id/view02"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:background="@drawable/leaf"
    android:layout_above="@id/view01"
    android:layout_alignLeft="@id/view01"
    />
<!-- 定义该组件位于view01组件的下方 -->
<TextView 
    android:id="@+id/view03"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:background="@drawable/leaf"
    android:layout_below="@id/view01"
    android:layout_alignLeft="@id/view01"
    />
<!-- 定义该组件位于view01组件的左边 -->
<TextView 
    android:id="@+id/view04"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:background="@drawable/leaf"
    android:layout_toLeftOf="@id/view01"
    android:layout_alignTop="@id/view01"
    />
<!-- 定义该组件位于view01组件的右边 -->
<TextView 
    android:id="@+id/view05"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:background="@drawable/leaf"
    android:layout_toRightOf="@id/view01"
    android:layout_alignTop="@id/view01"
    />      
</RelativeLayout>

网格布局


GridLayout是Android4.0新增的布局管理器,因此需要在Android4.0之后的版本中才能使用该布局管理器。如果希望在更早的Android平台上使用该布局管理器,则需要导入相应的支持库。

GridLayout的作用类似于HTML中的table标签,它把整个容器划分成row x columns个网格,每个网格可以放置一个组件。也可以设置一个组件横跨多少列、一个组件纵跨多少行。

# GridLayout的XML属性及相关方法说明

XML属性 相关方法 说明
android:alignmentMode setAlignmentMode(int) 设置该布局管理器采用的对齐模式
android:columnCount setColumnCount(int) 设置该网格的列数量
android:rowCount setRowOrderPreserved(boolean) 设置该网格的行数量
android:columnOrderPreserved setColumnOrderPreserved(boolean) 设置该网格容器是否保留列序号
android:rowOrderPreserved setRowOrderPreserved(boolean) 设置该网络容器是否保留行序号
android:useDefaultMargins setUseDefaultMargins(boolean) 设置该布局管理器是否使用默认的页边距

为了控制GridLayout布局容器中各子组件的布局分布,GridLayout提供了一个内部类:GridLayout.LayoutParams,该类提供了大量的XML属性来控制GridLayout布局容器中子组件的布局分布。

# GridLayout.LayoutParams的XML属性及相关方法说明

XML属性 相关方法 说明
android:layout_gravity setGravity 设置该子组件采用何种方式占据该网格的空间
android:layout_column 设置该子组件在GridLayout的第几列
android:layout_row 设置该子组件在GridLayout的第几行
android:layout_columnSpan 设置该子组件在GridLayout横向上跨几列
android:layout_rowSpan 设置该子组件在GridLayout纵向上跨几行

示例:

<?xml version="1.0" encoding="utf-8" ?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:rowCount="6"
    android:columnCount="4"
    android:id="@+id/root"
    >
<!-- 定义一个横跨4列的文本框,
并设置该文本框的前景色、背景色等属性  -->
<TextView 
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:layout_columnSpan="4"
    android:textSize="50sp"
    android:layout_marginLeft="4px"
    android:layout_marginRight="4px"
    android:padding="5px"
    android:layout_gravity="right"
    android:background="#eee"
    android:textColor="#000"
    android:text="0"/>
<!-- 定义一个横跨4列的按钮 -->
<Button 
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:layout_columnSpan="4"
    android:text="清除"/>
</GridLayout>

绝对布局


AbsoluteLayout,就是Android不提供任何布局控制,由开发者自己通过X坐标、Y坐标来控制组件的位置。当使用AbsoluteLayout作为布局容器时,布局容器不再管理子组件的位置、大小——这些都需要开发者自己控制。

注意:
大部分时候,使用绝对布局都不是一个好思路。因为运行Android应用的手机往往千差万别,因此屏幕大小、分辨率都可能存在较大差异,使用绝对布局会很难兼顾不同屏幕大小、分辨率的问题。因此AbsoluteLayout布局管理器已经过时。

使用绝对布局时,每个子组件都可指定如下两个XML属性:

  • layout_x:指定该子组件的X坐标
  • layout_y:指定该子组件的Y坐标

示例:

<?xml version="1.0" encoding="utf-8"?>
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<!-- 定义一个文本框,使用绝对定位 -->
<TextView 
    android:layout_x="20dip"
    android:layout_y="20dip"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="用户名:"
    />
<!-- 定义一个文本编辑框,使用绝对定位 -->
<EditText 
    android:layout_x="80dip"
    android:layout_y="15dip"
    android:layout_width="wrap_content" 
    android:width="200px" 
    android:layout_height="wrap_content" 
    />
<!-- 定义一个文本框,使用绝对定位 --> 
<TextView 
    android:layout_x="20dip"
    android:layout_y="80dip"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="密  码:"
    />
<!-- 定义一个文本编辑框,使用绝对定位 -->   
<EditText 
    android:layout_x="80dip"
    android:layout_y="75dip"
    android:layout_width="wrap_content" 
    android:width="200px" 
    android:layout_height="wrap_content" 
    android:password="true"
    />
<!-- 定义一个按钮,使用绝对定位 -->
<Button 
    android:layout_x="130dip"
    android:layout_y="135dip"
    android:layout_width="wrap_content" 
    android:layout_height="wrap_content" 
    android:text="登   录"
    />  
</AbsoluteLayout>

上面的界面布局中指定各组件的android:layout_xandroid:layout_y属性时指定了形如20dip这样的属性值,这是一个距离值。Android中一般支持如下常用的距离单位:

  • px(像素):每个px对应屏幕上的一个点
  • dip或dp(device independent pixels,设备独立像素):一种基于屏幕密度的抽象单位。在每英寸160的显示器上,1dip=1px。但随着屏幕密度的改变,dip与px的换算会发生改变。
  • sp(scaled pixels,比例像素):主要处理字体的大小,可以根据用户的字体大小首选项进行缩放
    -in(英寸):标准长度单位
  • mm(毫米):标准长度单位
  • pt(磅):标准长度单位,1/72英寸
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容