Android 的界面组件比较多,如果不理解它们内在关系,孤立地学习、记忆这些 UI 组件,不仅学习起来事倍功半,而且不利于掌握它们内在的关系。为了帮助读者更好地掌握 Android 界面组件的关系,我们将会把这些界面组件按照它们的关联分析,分为几组进行介绍。本次介绍的是第一组 UI 组件:以 ViewGroup 为基类派生的布局管理器。
为了更好地管理 Android 应用的用户界面里的各组件,Android 提供了布局管理器。通过使用布局管理器,Android 应用的图形用户界面具有良好的平台无关性。通常来说,推荐使用布局管理器来广利组件的分布、大小,而不是直接设置组件位置和大小。例如通过如下代码定义了一个文本框(TextView):
TextView hello = new TextView(this);
hello.setText("Hello Android");
为了让这个组件在不同的手机屏幕上都能运行良好 —— 不同手机屏幕的分辨率、尺寸并不完全相同,如果让程序手动控制每个组件的大小、位置,则将给编程带来巨大的困难。为了解决这个问题,Android 提供了布局管理器。布局管理器可以根据运行平台来调整组件的大小,程序员要做的,只是为容器选择合适的布局管理器。
与 Swing 界面编程不同的是,Android 的布局管理器本身就是一个 UI 组件,所有的布局管理器都是 ViewGroup 的子类。图 2.7 显示了 Android 布局管理器的类图。
从图 2.7 可以看出,所有布局都可作为容器类使用,因此可以调用多个重载的 addView() 向布局管理器中添加组件。实际上,我们完全可以用一个布局管理器嵌套到其他布局管理器中 —— 因为布局管理器也继承了 View,也可以作为普通 UI 组件使用。
1,线性布局
线性布局由 LinearLayout 类来代表,线性布局有点像 Swing 编程里的 Box,它们都会将容器里的组件一个挨着一个地排列起来。LinearLayout 可以控制各组件横向排列(通过设置 android:orientation 属性控制),也可控制各组件纵向排列。
Android 的线性布局不会换行,当组件一个挨着一个地排列到头之后,剩下的组件将不会被显示出来。
表 2.4 显示了 LinearLayout 支持的常用 XML 属性及相关方法的说明。
LinearLayout 包含的所有子元素都受 LinearLayout.LayoutParams 控制,因此 LinearLayout 包含的子元素可以额外指定如表 2.5 所示的属性。
基本上很多布局管理器提供了相应的 LayoutParams 内部类,该内部类用于控制它们的子元素支持指定 android:layout_gravity 属性,该属性设置该子元素在夫容器中的对齐方式。与 android:layout_gravity 相似的属性还有 android:gravity 属性(一般容器才支持指定该属性),android:gravity 属性用于控制它所包含的子元素的对齐方式。
例如定义如下 XML 布局管理器:
<?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>
上面的界面布局非常简单,它只是定义了一个简单的线性布局,并在线性布局中定义了 5 个按钮。定义线性布局时指定了垂直排列所有组件,而且所有组件对齐到容器底部并且水平居中,运行上面的程序,使用 Activity 显示上面的界面布局,将看到如图 2.8 所示界面。
如果将上面的布局文件中 android:gravity="bottom|center_horizontal" 改为 android:gravity="right|center_vertical" —— 也就是所有组件水平右对齐、垂直居中,再次使用 Activity 显示该界面布局将看到如图 2.9 所示界面。
从图 2.10 所示的运行接口可以看出,当采用线性布局来管理 5 个按钮组件时,由于这 5 个按钮组件无法在一行中同时显示出来,但 LinearLayout 不会换行显示多余的组件,因此图 2.10 中看不到第 4 和第 5 个按钮。
2,表格布局
表格布局由 TableLayout 所代表,TableLayout 继承了 LinearLayout,因此它的本质依然是线性布局管理器。表格布局采用行、列的形式来管理 UI 组件,TableLayout 并不需要明确地声明包含多少行、多少列,而是通过添加 TableRow,该 TableRow 就是一个表格行,TableRow 也是容器,因此它也可以不断地添加其他组件,每添加一个子组件该表格就增加一列。
如果直接向 TableLayout 中添加组件,那么这个组件将直接占用一行。
在表格布局中,列的宽度由该列中最宽的那个单元格决定,整个表格布局的宽度则取决于父容器的宽度(默认总是占满父容器本身)。
在表格布局管理器中,可以为单元格设置如下三种行为方式。
Shrinkable:如果某个列被设为 Shrinkable,那么该列的所有单元格的宽度可以被收缩,以保证该表格能适应父容器的宽度。
Stretchable:如果某个列被设为 Stretchable,那么该列的所有单元格的宽度可以被拉伸,以保证组件能完全填满表格空余空间。
Collapsed:如果某个列被设为 Collapsed,那么该列的所有单元格会被隐藏。
TableLayout 继承了 LinearLayout,因此它完全可以支持 LinearLayout 所支持的全部 XML 属性,除此之外,TableLayout 还支持如表 2.6 所示的 XML 属性。
下面的程序示范了如何使用 TableLayout 来管理组件的布局,下面是界面布局所使用布局文件。
<?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"
>
...<!-- 表格内容接下里详细讲解 -->
</TableLayout>
<!-- 定义第二个表格布局 ,指定第二列隐藏-->
<TableLayout android:id="@+id/TableLayout01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:collapseColumns="1"
>
...<!-- 表格内容接下来详细讲解 -->
</TableLayout>
<!-- 定义第三个表格布局 ,指定第2、3两列可以被拉伸-->
<TableLayout android:id="@+id/TableLayout01"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:stretchColumns="1,2"
>
...<!-- 表格内容接下里详细讲解 -->
</TableLayout>
</LinearLayout>
上面页面中定义了三个 TableLayout,三个 TableLayout 中指定了它们对各列的控制行为:
第一个 TableLayout,指定第 2 列允许收缩,第 3 列允许拉伸。
第二个 TableLayout,指定第 2 列被隐藏。
第三个 TableLayout,指定第 2 列和第 3 列允许拉伸。
接下来为布局中第一个 TableLayout 添加两行,第一行不使用 TableRow,直接添加一个 Button,那么该 Button 自己将占用整行。第二行先添加一个 TableRow,并未 TableRow 添加三个 Button,那说明该表格将包含三列。第一个表格的界面布局代码如下。
<!-- 直接添加按钮,它自己会占一行 -->
<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>
在上面的界面布局文件中我们直接把按钮上的文本写在布局文件中,这不是一种好的做法,因为 Android 推荐将这些字符串集中放到 XML 文件中管理。但此处为了编程简单,所以直接在 XML 布局文件中给出了按钮文本的字符串。
接下来为布局中第 2 个 TableLayout 添加两行,第 1 行不使用 TableRow,直接添加一个 Button,那么该 Button 自己将占用整行。第 2 行先添加一个 TableRow,并为 TableRow 添加三个 Button,那说明该表格将包含三列。第 2 个表格内容与第 1 个表格内容基本相似,但由于我们为第 2 个表格指定了 android:collapseColumns="1",这意味着第 2 行中间的按钮将会被隐藏。
接下来为布局中第 3 个 TableLayout 添加三行,第 1 行不使用 TableRow,直接添加一个 Button,那么该 Button 自己将占用整行。第 2 行先添加一个 TableRow,并未 TableRow 添加三个 Button,那说明该表格将包含三列。第 3 行也先添加一个 TableRow,并未 TableRow 添加两个按钮,则意味着表格的第 3 行只有两个单元格,第 3 个单元格为空。
第 3 个表格布局管理器的内容如下。
<!-- 直接添加按钮,它自己会占一行 -->
<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>
使用 Activity 来显示上面的界面布局将看到如图 2.11 所示界面。
3,帧布局
帧布局由 FrameLayout 所代表,FrameLayout 直接继承了 ViewGroup 组件。
帧布局容器为每个加入其中的组件创建一个空白的区域(称为一帧),每个子组件占据一帧,这些帧都会根据 gravity 属性执行自动对齐。帧布局的效果有点类似 AWT 编程的 CardLayout,都是把组件一个一个地叠加在一起,但 FrameLayout 则没有提供相应的方法。
表 2.7 显示了 FrameLayout 常用的 XML 属性及相关方法说明。
FrameLayout 包含的子元素也受 FrameLayout.LayoutParams 控制,因此它所包含的子元素可指定 android:layout_gravity 属性,该属性控制该子元素在 FrameLayout 中的对齐方式。
下面示范了帧布局的用法,可以看到 6 个 TextView 叠加在一起,上面的 TextView 遮住下面的 TextView。下面是使用帧布局的页面定义代码。
<?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>
上面的界面布局定义使用 FrameLayout 布局,并向该布局容器中添加了 7 个 TextView,这 7 个 TextView 的高度完全相同,而宽度则逐渐减少 —— 这样可以保证最先添加的 TextView 不会被遮挡;而且我们设置了 7 个 TextView 的背景色渐变。
使用 Activity 显示上面的界面布局,将看到如图 2.12 所示效果。
如果考虑轮换改变上面的帧布局中 6 个 TextView 的背景色,就会看到上面的颜色渐变条不断地变换,就像大街上的霓虹灯一样。下面的程序还是使用上面的 FrameLayout 布局管理器,只是程序启动了一条线程来控制周期性地改变这 6 个 TextView 的背景色。下面是该主程序的代码。
public class FrameLayoutTest extends Activity
{
private int currentColor = 0;
// 定义一个颜色数组
final int[] colors = new int[] {
R.color.color1,
R.color.color2,
R.color.color3,
R.color.color4,
R.color.color5,
R.color.color6
};
final int[] names = new int[] {
R.id.view01,
R.id.view02,
R.id.view03,
R.id.view04,
R.id.view05,
R.id.view06 };
TextView[] views = new TextView[names.length];
Handler handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// 表明消息来自本程序所发送
if (msg.what == 0x123)
{
for (int i = 0; i < names.length; i++)
{
views[i].setBackgroundResource(colors[(i
+ currentColor) % names.length]);
}
currentColor++;
}
super.handleMessage(msg);
}
};
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
for (int i = 0; i < names.length; i++)
{
views[i] = (TextView) findViewById(names[i]);
}
// 定义一个线程周期性的改变currentColor变量值
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
// 发送一条空消息通知系统改变6个TextView组件的背景色
handler.sendEmptyMessage(0x123);
}
}, 0, 200);
}
}
上面的程序中第 行代码定义了一个每 0.2 秒执行一次的任务,该任务仅仅向 Handler 发送一条消息,通知它更新 6 个 TextView 的背景色。
可能会有读者提出疑问:为何不直接在 run() 方法里直接更新 6 个 TextView 的背景色呢?这是因为 Android 的 View 和 UI 组件不是线程安全的,所以 Android 不允许开发者启动线程访问用户界面的 UI 组件。所以程序中额外定义了一个 Handler 来处理 TextView 背景色的更新。
上面的程序中直接使用 R.color.color1、R.color.color2、R.color.color3 等整形常量来代表颜色,这也得益于 Android 的资源访问支持。
简单地说,上免得程序通过任务调度控制了每隔 0.2 秒轮换更新一次 6 个 TextView 的背景色,这样看上去就像大街上的 “霓虹灯” 了。
4,相对布局
相对布局由 RelativeLayout 代表,相对布局容器内子组件的位置总是相对兄弟组件、父容器来决定的,因此这种布局方式被称为相对布局。
如果 A 组件的位置是有 B 组件的位置来决定的,Android 要去先定义 B 组件,再定义 A 组件。
RelativeLayout 可支持如表 2.8 所示的两个 XML 属性。
为了控制该布局容器中各子组件的布局分布,RelativeLayout 提供了一个内部来:RelativeLayout.LayoutParams,该类提供了大量的 XML 属性来控制 RelativeLayout 布局容器中子组件的布局分布。
RelativeLayout.LayoutParams 里只能设为 true、false 的 XML 属性如表 2.9 所示。
RelativeLayout.LayoutParams 里属性值为其他 UI 组件 ID 的 XML 属性如表 2.10 所示。
除此之外,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>
上面程序中第 14 行代码控制该组件位于父容器的中央,接下里定义的 4 个组件一次环绕在第一个组件四周。使用 Activity 来显示上面的布局文件可以看到如图 2.13 所示效果。
5,Android 4.0 新增的网格布局
网格布局由 GridLayout 代表,它是 Android 4.0 新增的布局管理器,因此需要在 Android 4.0 之后的版本中才能使用该布局管理器。如果希望在更早的 Android 平台上使用该布局管理器,则需要导入相应的支撑库。
GridLayout 的作用类似于 HTML 中的 table 标签,它把整个容器划分成 rows*columns 个网格,每个网格可以放置一个组件。除此之外,也可以设置一个组件横跨多少列、一个组件纵跨多少行。
GridLayout 提供了 setRowCount(int) 和 setColumnCount(int) 方法来控制该网格的行数量和列数量。
表 2.11 显示了 GridLayout 常用的 XML 属性及相关方法。
为了控制 GridLayout 布局容器中各子组件的布局分布,GridLayout 提供了一个内部来:GridLayout.LayoutParams,该类提供了大量的 XML 属性来控制 GridLayout 布局容器中子组件的布局分布。
表 2.12 显示了 GridLayout.LayoutParams 常用的 XML 属性及相关方法。
下面将会通过一个实例来示范 GridLayout 的功能和用法。
为了实现如图 2.14 所示的计算器界面,可以考虑将该界面分解成一个 6×4 的网格,其中第一个文本框横跨 4 列,第二个按钮横跨 4 列,后面每个按钮各占一格。
为了实现该界面,考虑如下不走来实现该界面。
(1),在布局管理器中定义一个 GridLayout,并在该 GridLayout 中依次顶一个文本框、按钮,该文本框、按钮各横跨 4 列。
(2),在 Java 代码中循环 16 次,依次添加 16 个按钮。
下面先定义如下界面文件:
<?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>
上面的界面布局文件中定义一个 6×4 的 GridLayout,并在该布局管理器中添加了两个子组件,其中第一个子组件横跨 4 列,第二个子组件也横跨 4 列。
接下来使用如下 Java 代码采用循环控制添加 16 个按钮。
public class GridLayoutTest extends Activity
{
GridLayout gridLayout;
// 定义16个按钮的文本
String[] chars = new String[]
{
"7" , "8" , "9" , "÷",
"4" , "5" , "6" , "×",
"1" , "2" , "3" , "-",
"." , "0" , "=" , "+"
};
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gridLayout = (GridLayout) findViewById(R.id.root);
for(int i = 0 ; i < chars.length ; i++)
{
Button bn = new Button(this);
bn.setText(chars[i]);
// 设置该按钮的字体大小
bn.setTextSize(40);
// 指定该组件所在的行
GridLayout.Spec rowSpec = GridLayout.spec(i / 4 + 2);
// 指定该组件所在列
GridLayout.Spec columnSpec = GridLayout.spec(i % 4);
GridLayout.LayoutParams params = new GridLayout.LayoutParams(
rowSpec , columnSpec);
// 指定该组件占满父容器
params.setGravity(Gravity.FILL);
gridLayout.addView(bn , params);
}
}
}
上面 for 循环中向 GridLayout 中添加了 16 个按钮,添加 16 个按钮时指定了每个按钮所在行号、列号,并指定这些按钮将会自动填充单元格的所有空间 —— 这样避免单元格中大量空白。运行该程序将可以看到如图 2.14 所示界面。
6,绝对布局
绝对布局由 AbsoluteLayout 代表。绝对布局就像 Java AWT 编程中的空布局,就是 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>
上面的绝对布局容器中的每个子组件都指定了 layout_x、layout_y 两个定位属性,这样才控制了每个子组件在容器中的出现位置。使用 Activity 显示上面的页面,将看到如图 2.15 所示界面。
不要以为笔者一下就通过绝对布局做出了图 2.15 所示的登录界面,实际上这个登录界面时不断调整各组件的位置,经过多次尝试之后得到的结果。当使用绝对布局来控制子组件布局时,编程要烦琐得多,而且在不同屏幕上的显示效果差异也很大。
上面的界面布局中指定各组件的 android:layout_x、android:layout_y 属性时指定了形如 20dip 这样的属性值,这是一个距离值。Android 中一般支持如下常用的距离单位。
px(像素):每个 px 对应屏幕上的一个点。
dip 或 dp(device independent pixels,设备独立像素):一种基于屏幕密度的抽象单位。在每英寸 160 点的显示器上,1 dip = 1 px。但随着屏幕密度的改变,dip 与 px 的换算会发生改变。
sp(scaled pixels,比例像素):主要处理字体的大小,可以根据用户的字体大小首选项进行缩放。
in(英寸):标准长度单位。
mm(毫米):标准长度单位。
pt(磅):标准长度单位,1/72 英寸。