文字:  背景:  字号:

 

第九章 窗口程序设计(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。
  [首页][上页][下页]