|
第九章 窗口程序设计(1)
Java的抽象窗口工具包AWT(Abstract Window
Toolkit)提供了创建基于窗口的图形用户界面的便利工具。它的内容相当丰富,共有60多个类和接口。利用AWT类库,用户可以方便地建立自己的窗口界面,响应并处理交互事件。
这一章我们将首先阐述有关窗口编程的基本概念,包括AWT的构成,窗口的布局显示以及交互事件的处理等。然后,我们通过实例介绍几个较为常用和重要的类,如菜单、对话框、滚动条等,最后还将说明如何利用AWT完成Java的动画功能。
9.1 基本概念
9.1.1 AWT类库的继承层次
图形窗口形式的用户界面不同于传统的命令行形式的用户界面,它通过“窗口”、“按钮”、“菜单”等可视的灵活方式提供人机交互的手段,更为直观和生动。Java的AWT包定义了窗口系统所显示的各种对象,既包括组织窗口屏幕元素所需的基本类,也包括图形处理,显示所需的基本类。 Java组织窗口元素是通过“组件”(Component)和“容器”(Container)来进行的,“组件”包括屏幕上的各种组成部件,如按钮、菜单、画布等。“容器”则是一种特殊的组件,用来放置、容纳其它组件或容器,如面板、对话框等。 用代表组件的最基本的类是Component。它是一个抽象类,封装定义了窗口中各种对象一系列最基本的属性和操作。除了有关菜单的类,其它所有代表窗口对象的类,都是类Component的子孙类,都继承了它的方法和性质。因而类Component的许多方法是程序中经常用的,在表9.1中列出了类Component的主要方法。
表9.1 类Component的主要方法
|
方 法 名 |
返回值类型 |
基本功能 |
| add |
void |
加入弹出式菜单 |
| addComponentListener |
void |
添加组件事件监听器 |
| addFocusListener |
void |
添加焦点事件监听器 |
| addKeyListener |
void |
添加键盘事件监听器 |
| addMouseListener |
void |
添加鼠标事件监听器 |
| addMouseMotionListener |
void |
添加鼠标动作事件监听器 |
| disableEvents |
void |
禁止处理某事件 |
| enableEvents |
void |
允许处理某事件 |
| getBackGround |
Color |
取组件背景色 |
| getComponent |
Component |
取包含某坐标值的组件 |
| getFont |
Font |
取组件字体 |
| getForeGround |
Color |
取组件前景色 |
| getLocation |
Point |
取组件坐标值 |
| getParent |
Container |
取组件父容器 |
| getPeer |
ComponentPeer |
取组件的对等对象 |
| getSize |
Dimension |
取组件大小 |
| getToolkit |
Toolkit |
取组件的工具包 |
| isEnabled |
boolean |
返回组件是否能使用的状态 |
| isShowing |
boolean |
返回组件是否正显示 |
| isVisible |
boolean |
检查组件是否可见 |
| list |
void |
列出组件清单 |
| paint |
void |
利用图形类显示组件 |
| processEvent |
void |
处理发生在组件内的事件 |
| repaint |
void |
重画组件 |
| setBackGround |
void |
设置组件背景色 |
| setFont |
void |
设置组件字体 |
| setForeGround |
void |
设置组件前景色 |
| setLocation |
void |
移动组件 |
| setSize |
void |
改变组件大小 |
| setEnabled |
void |
控制组件是否能被使用 |
| setVisible |
void |
设置组件是否可见 |
类Component的子类包括许多我们熟悉且常用的组件对象,如按钮类Button,标签类Label,选择框类Checkbox,画布类Canvas等。 Component还有一个特殊的子类Container作为最基本的组件容器。所有可作为容
器的窗口对象,都是由类Container或其子孙类生成的对象实体。Container的两个子类是类Window和类Panel。类Window是代表窗口的最基本的类,这又派生出两个子类:类Dialog用以制作对话框;类Frame则用以制作一般窗口。类Panel定义窗口上的子区域,用来分组安排窗口对象,更重要的,Panel是我们在第三章中提及的Java的特有程序类Applet的父类。通过Panel和Applet间的这种继承关系,Applet程序和AWT窗口环境结合在一起。因此正如我们在第三章示例中所见,Applet的外形和布局显示都是与一般窗口相似的,然而类Applet的有关内容是被封装在另一个包java.applet中的,我们将在下一章具体讲述。
图9.1给出的是Component的主要子类的继承层次图。
┌Button按钮 ├Convas画布 ┌─Dialog对话框 ├Choice弹出式列表框 ├─Frame基本框 ├Checkbox选择框 │ ├List列表框 ┌Window窗口 Component┼Container容器┤ 组件 ├Label标签 └Panel面板 ├TextArea多行 │ ├TextField单行文本 └Applet小应用程序 └Scrollbar滚动条 图9.1
Component主要子类继承层次图
AWT中中有关菜单的类独立于其它类,是从类MenuComponent中派生出来的。它们的继承关系如图9.2所示。
┌ MenuBar ┌Menu普通菜单 │ 菜单条 │ │ │ │ └PopupMenu object
─
MenuComponent┤ │ 弹出式菜单 对象 菜单组件 └ MenuItem-─┤ 菜单项 └checkboxMenuItem 可选菜单项 图9.2 菜单有关类继承层次图
AWT中的另一组类是有关图形显示的类,主要有图形类Graphics、字体类Font、颜色类Color和图像类Image。它们都从java.lang.Object类直接派出生来,完成窗口中的各种绘图、字串显示、颜色设置和图像载入功能。我们将在下面各节中结合对主要窗口组件类一起来介绍这些类。
9.1.2 窗口的布局与显示
窗口设计程序通常包括以下几方面的内容: ■设置基本容器窗口。
■设置容器布局。 ■添加所需组件。 ■交互事件处理。 1.基本窗口实例 我们来考察一个最简单的例子。 例9.1
WelcomWin.java程序文件 1:import java.awt.Frame; 2:import
java.awt.Graphics; 3:import
java.awt.enent.*; 4: 5:public class WelcomeWin extends
Frame{ 6: public static void main(String
args[]){//main方法,程序执行起点 7: WelcomeWin app=new
WelcomeWin(); 8: } 9: public
WelcomeWin(){//类WelcomeWin构造方法 10: super("My First
Try"); 11: pack(); 12: setSize(300,100); 13: addWindowListener(new
MyAdapter()); 14: setVisible(true); 15: } 16: public
void paint(Graphics
g){//类WelcomeWin显示方法 17: g.drawString("Welcome to Windows
Programming!",50,50); 18: } 19:} 20:class MyAdapter
extends WindowAdapter{//类MyAdapter处理交互事件 21: public void
windowClosing(WindowEvent
e{//窗口关闭事件 22: System.exit(0); 23: } 24:} 为了建立一个窗口,首先我们需要一个容器作为窗口的基本框架,通常通过派生或使用类Frame或Applet来作这个容器。在例9.1中,我们引入并派生了类Frame,设置为基本窗口(行1,行5)。为了处理交互事件,我们还引入了包java.awt.event(行3)。 例9.1仍旧使用读者已经熟悉的main方法来作为执行程序的起点(行6)。然而,这里的main方法只简单的创建了一个WelcomeWin的对象,所有实际的工作都在WelcomeWin的构造方法中完成。 行9~15是WelcomeWin的构造方法。它首先通过行10的语句: super("My
First Try!"); 调用类Frame的构造方法 Frame(String
title); 其中参数 “My First
Try!" 被设置为窗口的标题。接下来行11调用方法 pack(); 来为窗口“打包”,根据窗口中的组件设置窗口大小,并将组件组装起来。12行语句 setSize(300,150); 将窗口重新设定为300×150象素单位的规格。13行的语句 addWindowListener(new
MyAdapter()); 为创建的窗口添加了一个类MyAdapter的对象作为窗口事件监听器。14行使用方法 setVisible(true); 显示窗口。将例9.1程序编译运行,将显示出如图9.3的窗口。 细心的读者也许会觉得疑惑,在WelcomeWin的main方法和构造方法中都没有显式调用行14~16的paint方法,那么是如何在窗口上显示出字串“Welcome
to Windows
Programming!”的呢?实际上我们定义的类WelcomeWin通过继承类Frame成为类Component的间接子类,而Component的所有子类在被显示时都将自动调用paint方法。因此我们在例9.1程序中重写的方法paint在执行setVisible时被自动调用,显示出设定的字符串。 同样,在发生用户操作引发的窗口事件时,类MyAdapter作为监听器的对象将自动被调用,寻找相应的处理段。类MyAdapter的定义语句段为行20~24。这个类是专用于监听窗口事件的适配器类WindowAdapter的子类: class
MyAdapter extends
WindowAdapter WindowAdapter是一个虚类,包含一系列处理窗口事件的虚方法。这里我们只重写了它的一个方法,即行21~23的windowClosing方法: public
void windowClosing(WindowEvent
e) 方法所带的参数是一个WindowEvent类的对象。该类的对象代表在程序执行期间发生的窗口事件,譬如窗口的关闭或移动。本方法只处理窗口关闭事件,当用户以鼠标点击窗口最右上角的小图标 ,要求关闭窗口时被调用,通过语句
System.exit(0); 终止本次执行。这个简单的方法实际上是非常重要县城不可缺少的。读者可以尝试将程序中windowClosing方法去掉,再重新编译执行。我们发现窗口不再响应用户操作,无法将窗口关闭,处理一种“死机”状态。 例9.1程序展示了Java的窗口程序最基本最简单的结构,它所建立的窗口没有添加任何组件。下面我们就来进一步看看如何在窗口中添加组件,以及如何安排组件的位置。 2.组件和布局 在窗口上添加新的组件是通过调用容器基本类Container的方法 add(Component
comp); 进行的,其中Component类参数comp为添加的组件。由于我们用到的所有容器都从Container派生而来,因而使用add方法是十分方便有效的。同样,安排窗口上组件的位置是通过调用Container的另一个方法。 setLayout(LayoutManager
lmr); 对窗口本身设置布局来实现,其中setLayout为方法名,所带参数lmr为布局类对象。 在AWT中,布局类专门用以控制窗口的组件位置和大小。这样的布局类共有五种: ■BorderLayout ■CardLayout ■FlowLayout ■GridLayout ■GridBagLayout (1)BorderLayout是Frame对象的缺省布局设置,将窗口的区域分为东、南、西、北、中五部分,如图9.4所示。相应的组件加入时要以 add(String
direction,Component
comp); 的形式进行,其中字符型参数direction的取值为“East”、“West”、“South”、“North”、“Center”中任一种。
┏━━━━━━━━━━━━━━━━━━━━┓ ┃ North ┃ ┠----┬--------┬------┨ ┃West│ Center │East ┃ ┠----┴--------┴------┨ ┃ South ┃ ┗━━━━━━━━━━━━━━━━━━━━┛ 图9.4BorderLayout示意
(2)FlowLayout是Panel对象的缺省布局设置,它将使容器在添加组件时以从左至右。从上至下的顺序进行。例如程序段: Panel
p=new Panel(); p.add(new Button("Button 1")); p.add(new
Button("Button 2")); p.add(new Button("Button
3")); 依次生产三个Button对象,并将它们添加到Panel对象p中,p的显示结果如图9.5所示。
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ P ┃ ┃┏━━━━━━━┓┏━━━━━━━┓┏━━━━━━━┓┃ ┃┃Button1┃┃Button2┃┃Button3┃┃ ┃┗━━━━━━━┛┗━━━━━━━┛┗━━━━━━━┛┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
图9.5 FlowLayout示意
(3)GridLayout以n行×m列的方格形式组织容器布局,每一格的大小是一致的。其中n,m为创建该类对象时所带的参数。例如命令 setLayout(new
GridLayout(2,3)); 设置的布局形式为2行3列,如图9.6所示。
┏━━━━━┳━━━━━┳━━━━━┓
┃(0,0)┃(1,0)┃(2,0)┃ ┣━━━━━╋━━━━━╋━━━━━┫ ┃(0,0)┃(1,0)┃(2,0)┃ ┗━━━━━┻━━━━━┻━━━━━┛
图9.6 GridLayout示意
(4)CardLayout将容器内的组件设置为类似叠放的“卡片”的形式,一次只显示一张“卡片”。最初显示的是第一张,其余各张按序放置在第一张后面。CardLayout类提供若干特殊的方法实现这些“卡片”的显示,包括: next()显示下一组件 previous()显示前一组件 first()显示第一组件 last()显示最后一个组件 (5)GridBagLayout是最复杂也是最灵活的一个布局类。同GridLayout类似,它也以方格组织容器布局,然而每一个方格的大小可以是没的。用户可通过类GridBagConstraints来控制各方格的位置和大小。具体的使用我们将在后面的章节中详细叙述。 例9.2WindowsApp.java程序文件。 1:import
java.awt.*; 2:import
java.awt.event.*; 3://类WindowsApp实现ActionListener接口以处理按钮事件 4:public
class WindowsApp implements
ActionListener{ //两个用于显示的字符串 5: String
defaultText="Welcome to Windows Programming!"; 6: String
alterText="You will have lots of fun in Windows!"; 7: Label
label;//可改变显示的标签对象 8: 9: public static void main(String
args[]){//main方法 10: WindowsApp app=new
WindowsApp(); 11: } 12: 13: public WindowsApp(){
//构造方法 14: Frame f=new
Frame("Welcome"); 15: setup(f); 16: f.pack();//为窗口打包,根据窗口中的组件设置窗口大小并将组件组装起来,是java.awt.Window的方法 17: f.setSize(260,300); 18: f.addWindowListener(new
MyAdapter()); 19: f.setVisible(true); 20: } 21: void
setup(Frame f){ 22: label=new
Label();//设置标签 23: label.setText(defaultText); 24: Panel
buttons=new Panel(); 25: Button
b;//设置按钮 26: buttons.add(b=new
Button("Change")); 27: b.addActionListener(this); 28: buttons.add(b=new
Button("Exit")); 29: b.addActionListener(this); 30: f.add("Center",label);//加入标签和按钮 31: f.add("South",buttons); 32: } 33:
34: public void actionPerformed(ActionEvent e){//处理按钮事件
35: Button
b=(Button)e.getSource(); 36: if("Change".equals(b.getLabel())){//改变标签显示 37: if(defaultText.equals(label.getText())) 38: label.setText(alterText); 39: else
label.setText(defaultText); 40: } 41: else
if("Exit".equals(b.getLabel()))//关闭窗口,退出 42: System.exit(0); 43: } 44:} 45:class
MyAdapter extends WindowAdapter{ 46: public void
windowClosing(WindowEvent
e){//处理窗口关闭事件 47: System.exit(0); 48: } 49:} 在这个例子中,我们使用类Frame作为基本容器窗口,同样使用简单的main()方法作为程序执行的起点,不同的是我们在窗口中添加了三个组件:一个标签和两个按钮。语句行21~32的setup方法完成窗口的设置工作。 标签label首先由构造方法创建,然后通过Label类的setText方法设定标签上显示的字符串,由下列语句实现: 22:label=new
Label(); 23:label.setText(defaultText); 其中用于显示的字符串defaultText和alterText都定义为类WindowsApp的成员变量,在这个类里相当于全程常量使用。 按钮则通过在Panel类对象buttons中添加设置: 26:buttons.add(b=new
Button("Change")); 28:buttons.add(b=new
Button("Exit")); 其中传递给构造方法Button的字符串参数将显示在按钮上作为功能提示。由于Panel类的缺省布局为FlowLayout,两个按钮设置为左右并置的形式,且位置居于buttons的中部。最后我们将标签和按钮加入整个窗口(30~31行)
。由于Frame类的缺省布局为BorderLayout,语句 f.add("South",buttons); 将按钮置于屏幕下方。编译并运行例9.2程序,将在屏幕上得到显示结果如图9.7所示。 这里的事件处理过程也比例9.1复杂,它加入了对按钮事件的处理。在AWT中,程序执行期间某按钮被按下,产生的事件是ActionEvent类的对象。相应的监听处理接口为ActionListener,它定义的处理方法名为actionPerformed。因此为了方便的处理按钮事件,类WindowsApp完成了actionPerformed方法以实现ActionListener接口: 4:public
class WindowsApp implements ActionListener 34:public void
actionPerformed(ActionEvent
e) ActionEvent对象的方法getSource()返回一个Object对象,代表引发该ActionEvent事件的组件。在actionPerformed方法中,我们调用这个方法获取产生事件的组件并强制转换为Button类对象: 35:Button
b=(Button)e.getSource(); 接下来通过调用类Button的“获取按钮显示文本”方法getLabel,判别究竟是哪一个按钮被按下: public
String
getLabel() 这里获取的文本是与各按钮被创建时传递的字符串参数一致的。当按钮“Change“按下时,标签label的显示内容变换。这通过调用类Label的方法,即下面的“取标签中的显示文本”方法getText和“设定标签显示文本”方法setText public
String getText() public void setText(String
text) 来实现。而当按钮“Exit”被按下时,同窗口关闭事件一样,撤销窗口,终止执行。读者可试着按动“Change”按钮,将得到如图9.8的改换后的显示窗口。不断按动“Change”按钮,窗口来回变换。而挥动“Exit”按钮,效果与点击右上角的 图标是一样的:窗口撤销,执行结果。对关闭窗口事件的处理与例9.1一样,见行45~49。 [首页][上页][下页]
|