Android 学习之路 之 第1组UI组件:布局管理器(九)

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 英寸。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值