本文将要介绍的内容会比较“有趣”,因为我们可以看到非常熟悉的窗口、按钮、动画等效果,而这些图形界面元素不仅会让开发者感到更“有趣”,对最终用户也是一种诱惑,用户总是喜欢功能丰富、操作简单的应用,图形用户界面的程序就可以满足用户的这种渴望。

Java使用AWT和Swing类完成图形用户界面编程,其中AWT的全称是抽象窗口工具集(Abstract Window Toolkit),它是Sun最早提供的GUI库,这个GUI库提供了一些基本功能,但这个GUI库的功能比较有限,所以后来又提供了Swing库。通过使用AWT和Swing提供的图形界面组件库,Java的图形用户界面编程非常简单,程序只要依次创建所需的图形组件,并以合适的方式将这些组件组织在一起,就可以开发出非常美观的用户界面。

程序以一种“搭积木”的方式将这些图形用户组件组织在一起,就是实际可用的图形用户界面,但这些图形用户界面还不能与用户交互,为了实现图形用户界面的用户交互操作,程序还应为程序提供事件处理,事件处理负责让程序可以响应用户动作。

GUI(图形用户界面)和AWT

前面介绍的所有程序都基于命令行,基于命令行的程序可能只有一些“专业”计算机人士才会使用。例如前面编写的五子棋、×××等程序,恐怕只有我们自己才愿意玩这么“糟糕”的游戏,很少有最终用户愿意对着黑糊糊的命令行界面敲命令。

相反,如果为程序提供直观的图形用户界面(Graphics User Interface,简称为GUI),最终用户通过鼠标拖动、单击等动作就可以操作整个应用,整个应用程序就会受欢迎得多(实际上,Windows之所以广为人知,其最初的吸引力就是来自于它所提供的图形用户界面)。作为一个程序设计者,必须优先考虑用户的感受,一定要让用户感到“爽”,我们的程序才会被需要、被使用,这样的程序才有价值。

当JDK1.0发布时,Sun提供了一个基本的GUI类库,这个GUI类库希望可以在所有平台下都能运行,这套基本类库被称为“抽象窗口工具集(Abstract Window Toolkit)",它为Java应用程序提供了基本的图形组件。AWT是窗口框架,它从不同平台的窗口系统中抽取出共同组件,当程序运行时,将这些组件的创建和动作委托给程序所在的运行平台。简而言之,当使用AWT编写图形界面应用时,程序仅指定了界面组件的位置和行为,并未提供真正的实现,JVM调用操作系统本地的图形界面来创建和平台一致的对等体。

使用AWT创建的图形界面应用和所在运行平台有相同的界面风格,比如在Windows操作系统上,它就表现出Windows风格;在UNIX操作系统上,它就表现出UNIX风格。Sun希望采用这种方式来实现“Write Once, Run Anywhere”的目标。

但实际应用中,AWT出现了如下几个问题:

1、使用AWT作出的图形用户界面在所有平台上都显得很丑陋,功能也非常有限。

2、AWT为了迎合所有主流操作系统的界面设计,AWT组件只能使用这些操作系统上图形界面组件的交集,所以不能使用特定操作系统上复杂的图形界面组件,最多只能使用四种字体。

3、AW下用的是非常笨拙的、非面向对象的编程模式。

1996年,Netscape公司开发了一套工作方式完全不同的GUI库,简称为IFC (Internet Foundation Classes),这套GUI库的所有图形界面组件,例如文本框、按钮等都是绘制在空白窗口上的,只有窗口本身需要借助于操作系统的窗口实现。IFC真正实现了各种平台上的界面一致。不久,Sun和Netscape合作完善了这种方法,并创建了一个新的用户界面库:Swing, AWT, Swing、辅助功能API, 2D API以及拖放API共同组成了JFC (Java Foundation Classes,即Java基础类库),其中Swing组件全面替代了Java1.0中AWT组件,但保留了Java 1.1的AWT事件模型。总体上,AWT是图形用户界面编程的基础,Swing组件替换了绝大部分AWT组件,对AWT图形用户界面编程有几号的补充和加强。

Swing并没有完全替代AWT组件,而是建立在AWT基础之上,Swing仅提供了能力更强大的用户界面组件,即使是完全采用Swing编写的GUI程序中,依然需要使用AWT的事件处理机制。AWT组件在Swing里将有对应的实现,二者用法基本相似。

AWT容器

如果从程序员的角度来看一个窗口时,这个窗口不是一个整体(有点庖丁解牛的感觉),而是由多个部分组合而成的。

任何窗口都可被分解成一个空的容器,容器里盛装了大量基本组件,通过设置这些基本组件的大小、位置等属性,就可以将该空容器和基本组件组成一个整体的窗口。实际上,在笔者看来,图形界面编程非常简单,它非常类似于我们儿时玩的拼图游戏,容器类似于拼图的“母板”,而普通组件(如Button, List之类)则类似于拼图的图块。创建图形用户界面的过程就是完成拼图的过程。

容器(Container)是Component的子类,因此容器对象本身也是一个组件,具有组件的所有性质,可以调用Component的所有方法,Component有如下几个常用方法来设置组件的大小、位置和可见性等。

1、setLocation(int x, int y):设置组件位置。

2、setSize(int width, int height):设置组件的大小。

3、setBounds(int x, int y, int width, int height):同时设置组件的位置、大小。

4、setVisible(Boolean b):设置该组件的可见性。

除此之外,容器还具有能盛装其他组件的功能,容器类(Container)主要提供了如下几个常用方法来访问容器里的组件:

1、Component add(Component comp):向容器中添加其他组件(该组件既可以是普通组件,也可以是容器),并返回被添加的组件。

2、Component getComponentAt(int x, int y):返回指定点的组件。

3、int getComponentCount():返回该容器内组件的数量。

4、Component[] getComponents():返回该容器内的所有组件。

AWT主要提供了两种主要的容器类型:

1、Window:可独立存在的顶级窗口

2、Panel:可作为容器容纳其他组件,但不能独立存在,必须被添加到其他容器中(如Window, Panel或者Applet等)。

Frame、Panel、ScrolPane、Dialog是AWT编程中常用的组件,其中Frame代表常见的窗口,它是Window类的子类,具有如下几个特征;

1、Frame对象有标题,允许通过拖拉来改变窗口的位置、大小。

2、初始化时为不可见,可用setVisible(true)使其显示出来。

3、默认使用BorderLayout作为其布局管理器。

布局管理器

为了使生成的图形用户界面具有良好的平台无关性,Java语言中,提供了布局管理器这个工具来管理组件在容器中的布局,而不使用直接设置组件位置和大小的方式。例如我们通过如下定义了一个标签(Label):

Label hello=newLabel(“Hello Java”);

为了让这个hello标签里刚好可以容纳这个”Hello Java”字符串(既没有冗余空间,也没有内容被遮挡),也就是实现该标签的最佳大小,Windows可能应该设置为长100像素,高20像素即可,但换到Unix上,则可能需要设置为长120像素,高24像素。那么一个应用程序从Windows移植到Unix上时,程序需要做大量工作来调整图形界面。

对于不同的组件而言,它们都有一个最佳大小(既没有冗余空间,也没有内容被遮挡),这个最佳大小通常是平台相关的,程序在不同平台上运行时,相同内容的大小可能不一样。如果让程序手动控制每个组件的大小、位置,这将给编程带来巨大的困难,为了解决这个问题,Java提供了LayoutManager来解决这些问题,LayoutManager可以根据运行平台来调整组件的大小,程序员要做的,只是为容器选择合适的布局管理器。

所有的AWT容器都有默认的布局管理器,如果没有为容器指定布局管理器,该容器使用默认的布局管理器。为容器指定布局管理器通过调用容器对象的setLayoutManager(LayoutManager lm)方法来完成。如下代码所示:

c.setLayoutManager(new XxxLayout());

AWT提供了F1owLayout, BorderLayout, GridLayout, GridBigLayout, CardLayout五个常用的布局管理器,Swing还提供了一个BoxLayout布局管理器。下面将详细介绍这几个布局管理器。