1、 1 / 71第 5 章 Java 图形用户界面本章将用面向对象的编程方法来编写带图形用户界面的程序。图形用户界面(Graphical User Interface,GUI( 读作 goo-ee)为用户提供了一个直观、方便、快捷的图形化操作界面。借助 Java 中的菜单、输入区、按钮、对话框、窗口等标准界面元素可以创建个性化的图形用户界面,再加上鼠标、键盘等设备的操作,用户可以方便地向计算机系统发出指令,启动操作,并将系统运行的结果在同一个用户界面上显示出来。虽然现在有许多 Java 开发环境都能够自动或很方便地生成图形界面,但我们仍然有必要掌握 Java 的图形用户界面程序的基本原理。这包括
2、:Java 的图形用户界面程序有哪些基本组件类,这些类的层次关系是怎样的;在屏幕上如何摆放这些组件,怎样用这些基本的组件设计出专业的图形界面;这些组件如何响应用户的行为,也就是如何响应用户事件,即事件驱动原理。如果没有掌握这些基本内容,那么这方面的编程水平将停留在做“表面文章”上。核心内容:1. Swing 简介2. 图形用户界面与事件驱动的程序设计基础3. 布局管理器4. 事件处理机制5. Swing 容器6. Swing 基本组件7. Swing 高级组件8. Java 图形绘制5.1 Swing 简介应用程序的界面是人机之间的接口,早期的操作系统仅支持命令操作,对系统的任何操作都通过输入
3、命令来执行,也没有鼠标可以点击,系统对用户的命令也是以文本形式反馈,这样的界面即为文本界面。图形用户界面中的图形是相对于文本界面而言的,实际上主要指的是窗体。用户可以借助图形化的菜单、按钮等加上鼠标的点击向系统发出操作命令,并将程序运行结果通过图形界面反馈给用户。如今的操作系统都是基于窗口操作并提供鼠标支持,微软的操作系统更是直接命名为视窗(Windows)。当前几乎所有的桌面应用程序(Desktop Application)和客户端服务器程序(Client/Server Application)的客户端都采用图形用户界面。每种操作系统都会提供窗体部件 API(Application Prog
4、ramming Interface)以供系统自身调用显示和供程序开发人员创建 GUI 应用程序时调用。Java 在图形用户界面编程方面提供了功能强大的工具包。在 JDK1.0 中,图形组件存放在抽象窗口工具集 AWT(Abstract Window Toolkit)的库中。目标是希望构建一个通用的 GUI,使利用它编写的程序能够运行在所有的平台上,以实现 SUN 公司提出的口号“一次编写,随处运行” (Write once, run anywhere) 。考虑到跨平台的原因,AWT 只提供了各个平台都支持的,构建 GUI 必需的一些基本组件,因此 AWT 包小而简单。由于 AWT 直接调用本地
5、操作系统的图形构件来实现图形界面,使得用 AWT 构建的 GUI 往往在不同的操作系统平台上具有不同的风格,而且 GUI的性能也受到了限制。例如,生成一个 AWT 的复选框会导致 AWT 直接调用下层本地程序来生成一个复选框。不幸的是,一个 Windows 平台上的复选框同 Mac OS 平台或者各种UNIX 风格平台上的复选框并不是那么相同。其结果是一个 AWT 应用可能在 Windows 上表现很好,可是到了 Mac OS 上几乎不能使用,或者正好相反。这影响了 Java 程序的跨平台性。2 / 71AWT 最根本的缺点是在原生的用户界面之上仅提供了一个非常薄的抽象层。为了弥补这个不足,在
6、 JDK l.2 中,Sun 公司推出更加稳定、通用和灵活的用户界面组件库 Swing。Swing 是建立在 AWT 体系之上,完全用 Java 编写的一套轻量级的图形工具包。与 AWT 组件相比,Swing 组件占用的资源较少,类比较小,不借助本地系统来绘制自身。Swing 不仅重写了 AWT 的组件,还为这些组件增添了新的功能,并且提供了许多 AWT 没有的,创建复杂图形用户界面的组件,增强了 GUI 与 Java 程序的交互功能。大多数的Swing 组件都直接使用 Java 代码绘制,能够保持外观风格的一致性,可以更少地依赖平台和更少地使用本地的 GUI 资源。Swing 是 AWT 的
7、扩展,它提供了许多新的图形界面组件。为了与 AWT 组件类相区别,Swing GUI 组件类的开头都有前缀字母 J。例如按钮组件在 AWT 中是 Button 类,而在 Swing中为 JButton 类。 除了有与 AWT 类似的按钮(JButton)、标签(JLabel)、复选框(JCheckBox)、菜单(JMenu )等基本组件外,还增加了一个丰富的高层组件集合,如表格(JTable )、树(JTree)。与 AWT 的部件不同,许多 Swing 组件如按钮、标签,除了使用文字外,还可以使用图标修饰自己。Swing 组件被称为轻量组件(Lightweight Component) ,而
8、依赖平台的 AWT 组件被称为重量组件(Heavyweight Component)。但是,Swing 并没有代替 AWT。在 JDK l1 中,AWT 事件处理模型有了根本改变。Swing 使用的仍然是 Java 1 的事件处理模型。Swing 组件位于 javax 包中,AWT 组件位于 java 包中。为了使用 javax.swing 和 java.awt中的对象建立高效的图形用户界面,我们必须学习事件驱动的程序设计(Event-Driven Programming)的程序控制方式。事件在用户和 GUI 对象交互时产生。例如,当你移动光标,点击按钮或选择菜单时,事件就发生了。在事件驱动程
9、序中,通过定义事件处理方法,使对象对这些事件做出相应。在这一章中,我们将学习事件驱动程序设计的基本方法。几乎所有基于 GUI 的现代应用软件都是事件驱动的,因此学好这种程序设计风格是非常重要的。因此本章的学习目的是学习最常用的 GUI 对象和事件驱动的程序设计,而不是详尽地介绍 Swing 类。对于提到的类,也不会讨论其所有方法。本章的目的是为读者打一个坚实的基础,让读者熟悉其中的基本概念。如果你需要更复杂的功能,只要深入研究,Swing 几乎可以实现任何你想要的功能。5.2 图形用户界面与事件驱动的程序设计基础5.2.1 Java 图形用户界面的构成在 Java 中,GUI 组成元素都放在
10、java.awt 和 javax.swing 包内,它们都包含了大量的类。Java 的图形用户界面(GUI)由组件(Component)和容器( Container)构成。1. 组件(Component)组件是一个可以以图形化的方式显示在屏幕上并能与用户进行交互的对象,是构成图形用户界面的基本单位,里面不再包含其他成分。例如,按钮(JButton/Button ) , 标签(JLabel/Label)和文本框(JTextField/TextField)等类。2. 容器(Container)容器也是组件,它的最主要的作用是装载其它组件。容器是若干个组件和容器的集合。Java 提供了相应的容器类,
11、例如,框架(JFrame/Frame ) ,面板( JPanel/Panel)及滚动面板(JScrollPane/ScrollPane)等类。组件在容器中的位置和尺寸由布局管理器(Layout Manager)决定。3布局管理器(Layout Manager)为了使我们生成的图形用户界面具有良好的平台无关性,Java 语言中,提供了布局管理器3 / 71(LayoutManager)这个工具来管理组件在容器中的布局,而不使用直接设置组件位置和大小的方式。每个容器都有一个布局管理器,当容器需要对某个组件进行定位或判断其大小尺寸时,就会调用其对应的布局管理器。常用的布局管理器有顺序布局管理器(Fl
12、owLayout) ,网格布局管理器(GridLayout) ,边界布局管理器(BorderLayout )等。4事件处理机制(Event Handling)事件处理机制是用来接收和处理事件的机制。通过事件处理机制能够让生成的图形用户界面动起来,通过监听和响应鼠标,键盘等设备所触发的事件来完成系统所需要的各项功能。Java 采用委托事件处理模型来实现事件处理。5.2.2 Java 图形用户界面的实现使用 Swing 创建图形用户界面的一般步骤:1. 选取组件:创建组件对象。2. 设计布局:将组件对象按照一定布局加入到容器中。3. 响应事件: 初始化组件对象,并对组件对象注册某事件监听器。Swi
13、ng 图形用户界面程序设计有两个关键的方面。一方面是 GUI 对象在框架的内容面板上的布局,在 Swing 中通过布局管理器(Layout Manager)来实现;另一方面是处理这些 GUI 对象产生的事件,通过事件处理机制来实现。本章 5.3 和 5.4 节将分别介绍 Swing 布局管理器和事件处理机制。5.2.3 Swing 的类层次结构在 javax.swing 包中,定义了两种类型的组件:顶层容器(JFrame,JApplet,JDialog和 JWindow)和轻量级组件。 Swing 组件都是 AWT 的 Container 类的直接子类和间接子类。 swing 包是 Swing
14、 提供的最大包,它包含将近 100 个类和 25 个接口,几乎所有的 Swing 组件都在 swing 包中。图 5-1 和 5-2 描述了 Swing 与 AWT 的关系以及 Swing 组件层次结构。由图 5-1 可以看出,除了使用 Swing 包中的容器和组件外,Swing 仍然使用 AWT 包中的Font,Color, Graphics 等类设置字体,颜色和绘图。图 5-2 则列出了所有的 Swing 轻量级组件。4 / 71图 5-1 Swing 与 AWT 关系图图 5-2 Swing 组件层次图从图 5-2 可以看出,JComponent 是所有 Swing 轻量组件的父类。 J
15、Component 是一个抽象类,用于定义所有子类组件的一般方法。Swing 组件从功能上分可分为:1) 顶级容器:是放置其他 Swing 组件和容器的顶层容器。JFrame:框架窗体用于框架窗口的类,此窗口带有边框、标题、用于关闭和最小化窗口的图标等。通常用作 Swing 应用程序的主窗口。JDialog:对话框用作二级窗口。JApplet:Java 小程序用于能够在浏览器中显示的 Java 类。2) 中间容器:必须放置在上层容器中,不能独立存在,但是可以容纳其他组件和容器。JPanel:面板 最灵活、最常用的中间容器。JScrollPane:滚动面板 与 JPanel 类似,但还可在大的组
16、件或可扩展组件周围提供滚动条。JTabbedPane:选项板 包含多个组件,但一次只显示一个组件。用户可在组件之间方便地切换。JToolBar:工具栏 按行或列排列一组组件(通常是按钮) 。JSplitPane:分隔窗格 包含用分割栏(divider)分隔开的两个组件。3) 特殊容器:在 GUI 上起特殊作用的中间层,如 JInternalFrame,JLayeredPane,JRootPane。4) 基本控件:实现人际交互的组件,如 Jbutton, JComboBox, JList, JMenu, JSlider, JTextField。5 / 715) 不可编辑信息的显示:向用户显示不可
17、编辑信息的组件,例如 JLabel, JProgressBar, ToolTip。6) 可编辑信息的显示:向用户显示能被编辑的格式化信息的组件,如 JColorChooser, JFileChooser, JTable, JTextArea。5.2.4 使用 Swing 的基本规则 :与 AWT 组件不同,Swing 组件不能直接添加到顶层容器中,它必须添加到一个与Swing 顶层容器相关联的内容面板(content pane)上。内容面板是顶层容器包含的一个Container 类型的普通容器,它是一个轻量级组件。基本规则如下:(1)把 Swing 组件放入一个顶层 Swing 容器的内容面板
18、上;(2)避免使用非 Swing 的重量级组件。以顶层容器 JFrame 为例,首先创建一个 JFrame 对象 frame:JFrame frame = new JFrame();对 frame 添加组件有两种方式:1) 用 getContentPane( )方法获得 JFrame 的内容面板,再对其加入组件:frame.getContentPane().add(childComponent)2) 建立一个 JPanel 或 JDesktopPane 之类的中间容器,把组件添加到容器中,用setContentPane()方法把该容器置为 JFrame 的内容面板:JPanel content
19、Pane = new JPanel( );/把其它组件添加到 JPanel 中;frame.setContentPane(contentPane); /把 contentPane 对象设置成为 frame 的内容面板5.2.5 第一个 Swing 窗体应用程序例 5-1 在 Swing 窗体中显示“Hello,Swing!”的信息。当点击窗体右上方的“X”图标时,关闭该窗体。1 import javax.swing.*;2 public class HelloSwing 3 public static void main(String args) 4 JFrame frame = new JF
20、rame(“HelloSwing“);5 JLabel label = new JLabel(“Hello,Swing!“);6 frame.getContentPane().add(label);7 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);8 frame.setSize(300,200);9 frame.setVisible(true);10 11 程序运行效果如图 5-3:6 / 71图 5-3 第一个窗体应用程序程序分析:在该程序中,1)行 1 导入了 Swing 包,通常还需导入:import java.awt.*;im
21、port java.awt.event.* 。2)行 4 设置一个顶级框架窗体容器 JFrame 对象 frame。 3)行 5 创建一个 Swing 标签组件 JLabel 对象 label。4)行 6 将标签组件 label 添加到容器 frame 的内容窗格中;注意:在 Swing 中,JFrame 窗体中的组件不能直接添加到 JFrame 窗体对象中,需要通过调用 JFrame 窗体对象的getContentPane()方法获得该 JFrame 对象的内容窗格后,再调用内容窗格的 add()方法将组件添加到内容窗格中。5)行 7 通过调用 JFrame 窗体对象 frame 的 set
22、DefaultCloseOperation()方法,实现了在单击框架窗体右上方的“X”按钮时,可以关闭窗口的功能。在这里 setDefaultCloseOperation()的调用参数设置为 JFrame 的静态常量 EXIT_ON_CLOSE,表示当点击“X”按钮时将使用 System.exit(0) 方法退出应用程序。其他参数还包括: HIDE_ON_CLOSE, DO_NOTHING_ON_CLOSE 和 DISPOSE_ON_CLOSE。其中,HIDE_ON_CLOSE 是默认状态,表示当点击“X”按钮时,窗体将隐藏,但是应用系统并没有退出。 注意:如果使用的早期版本的平台,没有为窗体
23、对象提供 setDefaultCloseOperation()方法来关闭窗体。关闭窗口的功能则需要通过事件监听器实现。关于事件监听器详见本章 5.4.3部分。 6)行 8 设置窗体 frame 的长为 300 像素点,高为 200 像素点。7)行 9 setVisible()方法参数为 true 表示将显示该窗体对象。注意: JFrame 窗体对象创建后默认是隐藏的。8)以上是一个简单的 Swing 窗体应用程序。所有构建窗体和组件以及对窗体和组件进行设置的代码都放置在应用程序的 main()方法中。5.2.5 使用 Swing 创建窗体应用程序的实现步骤在例 5-1 中,我们使用 Swing
24、 组件创建了一个简单的窗体,该窗体中只有一个标签组件。通常情况下,窗体中的组件更多,布局更复杂,并且需要对事件进行监听、响应和处理。将所有构建窗体和组件以及对窗体和组件进行设置的代码都放置在应用程序的 main()方法中显然是不合适的。本章的其他示例将采用以下方法来创建窗体应用程序:1) 创建自定义窗体,继承 JFrame 类,并实现所需要的监听器接口(如果需要监听某一个或者多个事件);2) 将窗体中包含的控件组件,定义成自定义窗体类的私有成员变量;3) 在自定义窗体类的构造方法里设置窗体的布局管理器并初始化控件组件;7 / 714) 实现监听接口的方法;5) 在自定义窗体的 main()方法
25、中,创建自定义窗体的实例,并设置窗体的其他属性。5.3 布局管理器由于 Java 图形界面要考虑平台的适应性,因此,容器内元素的排列通常不采用直接通过坐标点确定部件位置的方式,而是采用特定的布局方式来安排部件。布局管理就是确定组件的尺寸和位置的过程。布局设计是通过为容器设置布局管理器来实现的。java.awt 包中共定义了 5 种布局管理器,与之对应有 5 种布局策略: 顺序布局(FlowLayout)、边界布局 (BorderLayout)、网格布局(GridLavout)、卡片布局(CardLayout) 以及网格块布局(GridBagLayout)。这些布局管理器在 Swing 程序中能
26、够继续使用,Swing 还增加了 BoxLayout(盒式布局) ,OverlayLayout(叠式布局)和 SpringLayout(弹式布局)等布局方式。通过 setLayout()方法可设置容器的布局方式。具体格式如下:public void setLayout(LayoutManager mgr)如不进行设定,则各种容器采用默认的布局管理器,窗体容器默认采用 BorderLayout,而面板容器默认采用 FlowLayout。当我们在容器中添加组件时,组件在容器中位置会按照布局管理器所指定的位置来放置。如果需要改变默认的布局管理器,我们需要重新设置布局方式。设置一个容器的布局方式步骤如
27、下:1)创建一个布局管理器对象,例如:BorderLayout b1 new BorderLayout();2)调用容器的 setLayout()方法,例如JPanel jp new JPanel();jp.setLayout(b1); 通过上面两步,即将 JPanel 对象 jp 的布局管理器从默认的 FlowLayout 改变为BorderLayout。也可将上面两步合为一步: JPanel jp new JPanel();jp.setLayout(new BorderLayout(); 这样代码看上去更加简洁清晰。5.3.1 FlowLayout 布局管理器FlowLayout 顺序布局
28、管理器是 JPanel 和 JApplet 的缺省布局管理器,是最简单的布局。在这种布局管理器中,组件按照它们添加到容器中的次序从左到右,从上向下依次放置。如果容器宽度不够则从一个新行开始继续排列组件。当容器被重置大小后,布局管理器会按照上述的布局规则重新调整布局,组件的大小不变,组件间相对位置将发生变化。将组件添加到具有 FlowLayout 布局管理器的容器时,缺省情况下是居中放置,用户也可以在构造方法中设置自己需要的布局方式:1 FlowLayout() 使得容器中的组件居中放置,组件间水平间距和垂直间距都为 5 个像素 FlowLayout (int align) 可设置容器中组件的对
29、齐方式,如左、右对齐,共有 5 种对齐方式:8 / 71FlowLayout.LEFT,FlowLayout.RIGHT,FlowLayout.CENTER,FlowLayout.LEADING,FlowLayout.TRAILING。组件间水平间距和垂直间距都为 5 个像素。FlowLayout(int align, int hgap, int vgap) 可设置容器内组件的对齐方式和组件间水平与垂直间距。例 5-2FlowLayout 示例:下例创建了一个包含 5 个按钮的使用顺序布局的框架窗体应用程序。本例是参照本章 5.2.5 节的步骤实现的。1 import java.awt.*;2
30、 import javax.swing.*;3 public class FlowWindow extends JFrame 4 public FlowWindow() 5 Container contentPane = getContentPane();6 contentPane.setLayout(new FlowLayout();7 contentPane.add(new JButton(“Button 1“);8 contentPane.add(new JButton(“Button 2“);9 contentPane.add(new JButton(“Button 3“);10 co
31、ntentPane.add(new JButton(“Button 4“);11 contentPane.add(new JButton(“Button 5“);12 13 public static void main(String args) 14 FlowWindow win = new FlowWindow();15 win.setTitle(“FlowLayout“);16 win.pack();17 win.setVisible(true);18 win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);19 20 程序运行效果如图 5-
32、4:图 5-4 FlowLayout 运行效果图程序分析:在该程序中,1)行 1,2 导入了 Java AWT 和 Swing 包。2)行 3 声明了类 FlowWindow,该类继承自 JFrame 类,表示该类是一个窗体类。由于在该类中没有事件处理代码,所以该类不需要实现任何事件监听器。3)行 4-12 是 FlowWindow 类的构造方法。在构造方法中,首先行 5 获得了该框架窗体的内容面板,然后行 6 设置布局管理器为顺序布局。注意:这里只能对内容面板设置布局管理器,而不能直接对框架窗体设置。行 7-11 分别向内容面板中添加了 5 个按钮。4)行 13-19 是该类的主方法。在 m
33、ain()方法中,创建了一个 FlowWindow 对象 win,然后分别对 win 的标题和显示模式等属性进行设置。注意:框架窗体的 pack()方法表示框架9 / 71窗体将设定一个适当的大小,以便能够以“紧缩”的形式包容各个组件。当使用 pack()方法时,就不需要调用 setSize()的方法来设置大小了。因此如图 5-4 所示,win 窗体在一行中容纳 5 个按钮,窗体中没有其它多余的空间。在如图 5-4 的情况下, 如果对框架窗体进行缩小的鼠标操作,5 个按钮大小保持不变,按钮的位置会相应地调整,如图 5-5 所示。如果进一步缩小,可出现如图 5-6 所示的情况。可以看出,该窗体采
34、用的是默认居中的顺序布局方式。图 5-5 框架的宽度被缩小 图 5-6 框架的宽度进一步被缩小5.3.2 BorderLayout 布局管理器BorderLayout 布局管理器是 JFrame 和 JDialog 的缺省布局管理器。采用布局管理器的容器将划分为五个区域:North、South、West、East 和 Center,它们分别代表上北、下南、左西、右东和居中五个区域,如图 5-7 所示。图 5-7 BorderLayout 区域划分当拉伸使用 BorderLayout 布局管理器的容器,各组件相对位置不变,组件尺寸发生变化:East 和 west 位置的组件宽度不变,高度改变,而
35、 South 和 North 位置的组件高度不变,宽度改变。倘若上面 5 个区域中有些区域没有被设置,组件会自动地扩展,遵循的原则是“边上没有中间补,中间没有则空” 。也就是说 5 个区域中如果缺少 East 或 west 区域,会用 Center位置上放置的组件进行延伸填充;如果缺少 South 或 North,East, west 和 Center 位置上放置的组件会共同延伸进行填充;若缺少 Center 部分,该区域就空着。BorderLayout 的构造方法如下:1 BorderLayout()使用默认设置所包含的组件间没有距离;2 BorderLayout(int hgap,int
36、vgap) 可设置容器中各组件的水平与垂直间距;使用 BorderLayout 布局管理器时,在添加组件时需指明添加的位置,否则就不能显示10 / 71该组件,可以用 BorderLayout 的 5 个 static 的成员 EAST、WEST、SOUTH 、NORTH 和CENTER 表示五个区域。从 JDKl.4 开始,北、南、西、东四个区有了新名字,分别是页首(PAGESTART)、页尾(PAGEEND)、行首(LINE START)、行尾(LINEEND)。例 5-3BorderLayout 示例: 本例将例 5-2 改写成 BorderLayout 布局方式。1 import ja
37、va.awt.*;2 import javax.swing.*;3 public class BorderWindow extends JFrame 4 public BorderWindow() 5 Container contentPane = getContentPane();6 contentPane.setLayout(new BorderLayout();7 contentPane.add(new JButton(“Button 1(NORTH)“),BorderLayout.NORTH);8 contentPane.add(new JButton(“Button 2(CENTER
38、)“),BorderLayout.CENTER);9 contentPane.add(new JButton(“Button 3(WEST)“),BorderLayout.WEST);10 contentPane.add(new JButton(“Button 4(SOUTH)“),BorderLayout.SOUTH);11 contentPane.add(new JButton(“Button 5(EAST)“),BorderLayout.EAST);12 13 public static void main(String args) 14 BorderWindow win = new B
39、orderWindow();15 win.setTitle(“BorderLayout“);16 win.pack();17 win.setVisible(true);18 win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);19 20 程序运行效果如图 5-8:图 5-8 BorderLayout 运行效果图程序分析:在该程序中,行 6 设置该框架窗体的布局为 BorderLayout。并在行 7-11 中,将 5 个按钮分别放置到 NORTH,CENTER, WEST, SOUTH 和 EAST 五个区域中。注意:这里 add()方法的第 2 个参数用于设置组件在边界布局中的方位。读者思考:该程序中行 6 可以不要。为什么?5.3.3 GridLayout 布局管理器Grid 在英文中是格子的意思。 GridLayout 布局管理器将容器划分为网格,在使用