AWT组件库 JDK提供了能创建图形用户界面的许多组件。本模块考察这些AWT组件,以及非组件的AWT类,例如Color、Font和图形用户界面的打印。 第一节 相关问题 讨论-下面的问题和本模块中的内容有关: 你现在已经知道如何为图形化输出和交互式用户输入来创建Java图形用户界面。然而,我们只介绍了能创建图形用户界面的一些组件。其他的组件在图形用户界面中有什么用处呢? 第二节 目 标 完成本模块之后,你应该能够: 认识关键的AWT组件。 给你一个用户界面的描述,能够用AWT组件来创建一个用户界面。 给你一个AWT程序,能够改变AWT组件的颜色和字体。 使用Java打印机制来打印一个用户界面。 第三节 AWT的特点 AWT提供了各种标准特性。本模块将介绍你可以使用的组件,并且概述了你需要知道的一些特殊情形。 首先将描述AWT的各个组件。它们用来创建用户界面。你需要知道所有图形用户界面组件,这样你就可以在创建你自己的界面时选择合适的组件。 AWT组件提供了控制界面外观的机制,包括用于文本显示的颜色和字体。 此外,AWT还支持打印。这个功能是在JDK1.1版中引入的。 10.3.1 按钮(Button) 你已经比较熟悉Button组件了。这个组件提供了“按下并动作”的基本用户界面。可以构造一个带文本标签的按钮,用来告诉用户它的作用。 Button b = new Button("Sample"); b.addActionListener(this); add(b); 任何实现了被注册为监听者的ActionListener接口的类,它的actionPerformed()方法将在一个按钮被鼠标点击“按下”时被调用。 public void actionPerformed(ActionEvent ae) { System.out.println("Button press received."); System.out.println("Button's action command is: " + ae.getActionCommand()); } 按钮被按下时调用的getActionCommand()方法在缺省情况下将返回标签字符串。用按钮的setActionCommand()方法改变动作命令和标签。 Button b = new Button("Sample"); b.setActionCommand("Action Command Was Here!"); b.addActionListener(this); add(b); 注-SampleButton和ActionCommandButton的完整源代码可以在course_examples目录下找到。 10.3.2 复选框(Checkbox) Checkbox组件提供一种简单的“开/关”输入设备,它旁边有一个文本标签。 Frame f = new Frame("Checkbox") Checkbox one = new Checkbox("One", true); Checkbox two = new Checkbox("Two", false); Checkbox three = new Checkbox("Three", true); one.addItemListener(this); two.addItemListener(this); three.addItemListener(this); f.add(one); f.add(two); f.add(three); 选取或不选取(取消)一个复选框的事件将被送往ItemListener接口。所传递的ItemEvent包含getStatechange()方法,它根据实际情况返回ItemEvent.DESELECTED或ItemEvent.SELECTED。getItem()方法将受到影响的复选框作为一个表示这个复选框标签的String对象返回。 class Handler implements ItemListener { public void itemStateChanged(ItemEvent ev) { String state = "deselected"; if (ev.getStateChange() == ItemEvent.SELECTED){ state = "selected"; } System.out.println(ev.getItem() + " " + state); } } 10.3.3 复选框组-单选框(Checkbox group-Radio Button) 复选框组提供了将多个复选框作为互斥的一个集合的方法,因此在任何时刻,这个集合中只有一个复选框的值是true。值为true的复选框就是当前被选中的复选框。你可以使用带有一个额外的CheckboxGroup参数的构造函数来创建一组中的每个复选框。正是这个CheckBoxGroup对象将各个复选框连接成一组。如果你这么做的话,那么复选框的外观会发生改变,而且所有和一个复选框组相关联的复选框将表现出“单选框”的行为。 Frame f = new Frame("Checkbox Group"); CheckboxGroup cbg = new CheckboxGroup(); Checkbox one = new Checkbox("One", false, cbg); Checkbox two = new Checkbox("Two", false, cbg); Checkbox three = new Checkbox("Three", true, cbg); one.addItemListener(this); two.addItemListener(this); three.addItemListener(this); f.add(one); f.add(two); f.add(three); 10.3.4 下拉列表(Choice) 下拉列表组件提供了一个简单的“从列表中选取一个”类型的输入。例如: Frame f = new Frame("Choice"); Choice c = new Choice(); c.add("First"); c.add("Second"); c.add("Third"); c.addItemListener(this); f.add(c); 点击下拉列表组件时,它会显示一个列表,列表中包含了所有加入其中的条目。注意所加入的条目是String对象。 ItemListener接口用来观察下拉列表组件的变化,其细节和复选框的相同。 10.3.5 画布(Canvas) 画布提供了一个空白(背景色)的空间。除非你用setSize()显式地定义它的大小,否则它的大小就是0×0。 画布的空间可以用来绘图、显示文本、接收键盘或鼠标的输入。后面的模块将告诉你如何有效地在AWT中绘图。 通常,画布用来提供一个一般的绘图空间或者为客户组件提供工作区域。 画布可以“监听”所有适用于一个普通组件的事件。特别地,你还可能想增加KeyListener、MouseMotionListener和MouseListener对象,以允许某种方式对用户输入作出反应。 注-如要在画布中接收键盘事件,必须调用画布的requestFocus()方法。如果缺少这个步骤,一般是不能将击键“导向”画布的。键盘事件会送往另一个组件,或整个地丢失了。示例代码overleaf(第228页)表明了这一点。 下面是画布的一个范例。每击一次键,这个程序就改变一次画布的颜色。 import java.awt.*; import java.awt.event.*; import java.util.*; public class MyCanvas extends Canvas implements KeyListener { int index; Color colors[] = {Color.red, Color.green, Color.blue }; public void paint(Graphics g) { g.setColor(colors[index]); g.fillRect(0,0,getSize().width,getSize().height); } public static void main(String args[]) { Frame f = new Frame("Canvas"); MyCanvas mc = new MyCanvas(); f.add(mc,BorderLayout.CENTER); f.setSize(150, 150); mc.requestFocus(); mc.addKeyListener(mc); f.setVisible(true); } public void keyTyped(KeyEvent ev) { index++; if (index == colors.length) { index =0; } repaint(); } public void keyPressed(KeyEvent ev) { } public void keyReleased(KeyEvent ev) { } } 10.3.6 标签(Label) 一个标签对象显示一行静态文本。程序可以改变文本,但用户不能改变。标签没有任何特殊的边框和装饰。 Label l = new Label( " Hello " ); add(l); 标签通常不处理事件,但也可以按照和画布相同的方式来处理事件。也就是说,只有调用了requestFocus()方法后,才能可靠地检取击键事件。 10.3.7 文本域(Textfield) 文本域是一个单行的文本输入设备。例如: TextField f = new TextField("Single line" " , 30); f.addActionListener(this); add(f); 因为只允许有一行,所以当按下Enter或Return键时,ActionListener可以通过actionPerformed()知道这个事件。如果需要,还可以增加其他的组件监听者。 除了注册一个ActionListener,你还可以注册一个TextListener来接收关于个别击键的通知。它的回调方法是textValueChanged(TextEvent)。 10.3.8 文本区(TextArea) 文本区是一个多行多列的文本输入设备。你可以用setEditable(boolean)将它设置成只读的。文本区将显示水平和垂直的滚动条。 下面这个范例创建了一个4行×30字符的文本,最初它含有“Hello!”。 TextArea t = new TextArea( " Hello! " , 4, 30); t.addTextListener(this); add(t); 你用addTexListener指定的监听者将以和文本域相同的方式接收到关于击键的通知。 你可以给文本区增加一般的组件监听者,然而,由于文本是多行的,按下Enter键将导致把另一个字符送入缓冲。如果你需要识别“输入的结束”,你可以在文本区旁放置一个“应用”或“确认”按钮,以便用户指明“输入的结束”。 10.3.9 文本组件(Text Components) 文本区和文本域的文档都分为两个部分。如果你查找一个称为文本组件的类,你会找到若干个文本区和文本域共有的方法。例如,文本区和文本域都是文本组件的子类。 你已经知道文本区和文本域类的构造函数允许你指定显示所用的列数。记住所显示的组件大小是由布局管理器决定的,因此这些设置可能被忽略。进而,列数是按所用字体的平均宽度计算的。如果使用一种空间比例字体,实际显示的字符数可能相差很大。 由于文本组件实现了TextListener,诸如文本域、文本区及其它子类都有对击键事件的内置支持。 10.3.10 列表(list) 一个列表将各个文本选项显示在一个区域中,这样就可以在同时看到若干个条目。列表可以滚动,并支持单选和多选两种模式。例如: List l = new List(4, true); 1.add("Hello"); 1.add("there"); 1.add("how"); 1.add("are");  构造函数的数值参数定义了按可见列计算的列表高度。这个值也可能被布局管理器覆盖。一个值为true的布尔型参数表明这个列表允许用户作多个选择。  选取或取消一个条目时,AWT将一个ItemEvent的实例送往列表。用户双击滚动列表中的一个条目时,单选模式和多选模式的列表都会产生一个ActionEvent。根据每个平台的约定来决定列表中的条目是否被选取。对于UNIX/Motif环境,单击会加亮列表中的一个条目,只有双击才会触发列表事件(选取条目)。 10.3.11 对话框(Dialog) 对话框组件与一个框架相关联。它是一个带有一些装饰的自由窗口。它与框架的区别在于所提供的一些装饰,而且你可以生成一个“模式”对话框,它在被关闭前将存储所有窗口的输入。 对话框可以是无模式和模式的。对于无模式对话框,用户可以同时与框架和对话框交互。“模式”对话框在被关闭前将阻塞包括框架在内的其他所有应用程序的输入。 由于对话框是窗口的子类,所以它的缺省布局管理器是Border Layout。 Dialog d = new Dialog(f, "Dialog", false); d.add(new Label("Hello, I'm a Dialog",Border.Layout.CENTER)); d.pack(); 对话框在创建时通常是不可见的。通常在对按下按钮等用户输入作出反应时,才显示对话框。 public void actionPerformed(ActionEvent ev) { d.setVisible(true); } 注-可以把对话框作为一个可重用设备。也就是说,在显示上关闭对话框,并不销毁这个对象;保存这个对象以便以后使用。垃圾回收站使得浪费内存变得非常容易对付。记住,创建和初始化对象耗费时间,所以不应该不加考虑就进行创建和初始化。 要隐藏对话框,你必须调用setVisible(false)。典型的做法是对它添加一个WindowListener,并等待对那个监听者的windowClosing()调用。这和处理一个框架的关闭是平行的。 10.3.12 文件对话框(File Dialog) 文件对话框是文件选择设备的一个实现。它有自己的自由窗口,以及窗口元素,并且允许用户浏览文件系统,以及为以后的操作选择一个特定的文件。例如: FileDialog d = new FileDialog(parentFrame, "FileDialog"); d.setVisible(true);// block here until OK selected String fname = d.getFile();  通常并不需要处理FileDialog的事件。调用setVisible(true)将阻塞事件,直至用户选择OK,这时会请求用户选择的文件名。这个信息将作为一个String返回。 10.3.13 滚动面板(Scroll Pane) 滚动面板提供了一种不能作为自由窗口的通用容器。它应当总是和一个容器相关联(例如,框架)。它提供了到一个更大的区域的视窗以及操纵这个视窗的滚动条。例如: Frame f = new Frame("ScrollPane"); Panel p = new Panel(); ScrollPane sp = new ScrollPane(); p.setLayout(new GridLayout(3, 4)); sp.add(p); f.add(sp, "Center"); f.setSize(200, 200); f.serVisible(true); 滚动面板创建和管理滚动条,并持有一个组件。你不能控制它所用的布局管理器。你可以将一个面板加入到滚动面板中,配置面板的布局管理器,并在那个面板中放置你的组件。 通常,你不处理滚动面板上的事件;这些事件通过滚动面板所包含的组件进行处理。 第四节 菜 单 菜单与其他组件有一个重要的不同:你不能将菜单添加到一般的容器中,而且不能使用布局管理器对它们进行布局。你只能将菜单加到一个菜单容器中。然而,你可以将一个Jmenuswing组件加到一个Jcontainer中。你可以通过使用setMenuBar()方法将菜单放到一个框架中,从而启动一个菜单“树”。从那个时刻之后,你可以将菜单加到菜单条中,并将菜单或菜单项加到菜单中。 弹出式菜单是一个例外,因为它们可以以浮动窗口形式出现,因此不需要布局。 10.4.1 帮助菜单 菜单条的一个特性是你可以将一个菜单指定为帮助菜单。这可以用setHelpMenu(Menu)来做到。要作为帮助菜单的菜单必须加入到菜单条中;然后它就会以和本地平台的帮助菜单同样的方式被处理。对于X/Motif类型的系统,这涉及将菜单条放置在菜单条的最右边。 10.4.2 菜单条(MenuBar) 一个菜单条组件是一个水平菜单。它只能加入到一个框架中,并成为所有菜单树的根。在一个时刻,一个框架可以显示一个菜单条。然而,你可以根据程序的状态修改菜单条,这样在不同的时刻就可以显示不同的菜单。例如: Frame f = new Frame("MenuBar"); MenuBar mb = new MenuBar(); f.setMenuBar(mb);  菜单条不支持监听者。作为普通菜单行为的一部分,在菜单条的区域中发生的预期事件会被自动处理。 10.4.3 菜单 菜单组件提供了一个基本的下拉式菜单。它可以加入到一个菜单条或者另一个菜单中。例如: MenuBar mb = new MenuBar(); Menu m1 = new Menu("File"); Menu m2 = new Menu("Edit"); Menu m3 = new Menu("Help"); mb.add(m1); mb.add(m2); mb.setHelpMenu(m3); f.setMenuBar(mb);  注-这里显示的菜单是空的,这正是File菜单的外观。 你可以将一个ActionListener加入到菜单对象,但这种做法是罕见的。正常情况下,菜单用来显示和控制菜单条,这将在后面讨论。 10.4.4 菜单项(MenuItem) 菜单项组件是菜单树的文本“叶”结点。它们通常被加入到菜单中,以构成一个完整的菜单。例如: Menu m1 = new Menu("File"); MenuItem mi1 = new MenuItem("New"); MenuItem mi2 = new MenuItem("Load"); MenuItem mi3 = newMenuItem ("Save"); MenuItem mi4 = new MenuItem("Quit"); mi1.addActionListener(this); mi2.addActionListener(this); mi3.addActionListener(this); m1.add(mi1); m1.add(mi2); m1.addSeparator(); m1.add(mi3); 通常,将一个ActionListener加入到一个菜单项对象中,以提供菜单的行为。 10.4.5 复选菜单项(CheckboxMenuItem) 复选菜单项是一个可复选的菜单项,所以你可以在菜单上有选项(“开”或“关”)。例如: Menu m1 = new Menu("File"); MenuItem mi1 = new MenuItem("Save"); CheckboxMenuItem mi2 = new CheckboxMenuItem("Persistent"); mi1.addItemListener(this); mi2.addItemListener(this); m1.add(mi1); m1.add(mi2);  应当用ItemListener接口来监视复选菜单。因此当复选框状态发生改变时,就会调用itemStateChanged()方法。 10.4.6 弹出式菜单(PopupMenu) 弹出式菜单提供了一种独立的菜单,它可以在任何组件上显示。你可以将菜单条目和菜单加入到弹出式菜单中去。 例如: Frame f = new Frame("PopupMenu"); Button b = new Button("Press Me"); PopupMenu p = new PopupMenu("Popup"); MenuItem s = new MenuItem("Save"); MenuItem l = new MenuItem("Load"); b.addActionListener(this); f.add(b,Border.Layout.CENTER); p.add(s); p.add(l); f.add(p); 为了显示弹出式菜单,你必须调用显示方法。显示需要一个组件的引用,作为x和y坐标轴的起点。通常,你要为此使用组件的触发器。在上面这个范例中,触发器是Button b。 弹出式菜单(续) public void actionPerformed(ActionEvent ev) { p.show(b, 10, 10); // display popup // at (10,10) relative to b }  注-弹出式菜单必须加入到一个“父”组件中。这与将组件加入到容器中是不同的。在上面这个范例中,弹出式菜单被加入到周围的框架中。 第五节 控制外观 你可以控制在AWT组件中所显示的文本的前景背景颜色、背景颜色和字体。 10.5.1 颜色 有两个方法用来设置组件的颜色: setForeground() getForeground() 这两个方法都带有一个参数,参数是java.awt.Color类的实例。你可以使用常数颜色,如Color.red,Color.blue等。所有预定义的颜色列在Color类的文档中。 此外,你可以创建一个特定的颜色,例如: int r = 255, g = 255, b = 0; Color c = new Color(r, g, b); 上述构造函数根据指定的红色、绿色和蓝色的亮度(它们的范围都是0~255)来创建一个颜色。 10.5.2 字体 在组件中显示时所用的字体可以用setFont()方法来设置。这个方法的参数应当是java.awt.Font类的实例。 没有为字体定义常数,但你可以根据字体的名称,风格,磅值来创建一个字体。 Font f = new Font("TimesRoman", Font.PLAIN, 14); 有效的字体名称包括: Dialog Helvetica TimesRoman Courier 可以通过调用Toolkit对象的getFontlist()方法来得到完整的列表。GetToolkit()方法是用来在显示toolkit后获得toolkit的。还有另外一种方法,你可以使用缺省的toolkit,它可以通过调用Toolkit.getDefaultToolkit()来获得。 字体风格常数实际上是int值,即: Font.BOLD Font.ITALIC Font.PLAIN Font.BOLD+ Font.ITALIC 磅值必须使用int值来指定。 第六节 打 印 在JDK1.2中的打印处理方式和屏幕显示是几乎平行的。会获取一种特殊的java.awt.Graphics对象,这样任何送往图形的绘图指令实际上会最终被送往打印机。 JDK1.2打印系统允许使用本地打印机的控制约定。选择一个打印操作时,用户会看到一个打印机选择对话框。然后用户可以设置各种选项,如:纸张大小、打印质量和使用哪个打印机。例如: Frame f = new Frame("Print test"); Toolkit t = f.getToolkit(); PrintJob job = t.getPrintJob(f, "MyPrintJob", null); Graphics g = job.getGraphics(); 这些代码创建了一个Graphics对象,它将连接到用户选择的打印机。 你可以使用任何Graphics类绘图方法来使用打印机。另一种方法,如下所示,你可以让一个组件在图形上绘制自身。 f.printComponents(g); print()方法以这种方式让组件绘制自身,但它只和所要求的组件相关联。如果是容器,你可以使用printcomponents()方法使容器和它所包含的全部组件绘制在打印机上。 g.dispose(); job.end(); 创建输出页之后,使用dispose()方法将页面提交给打印机作业对象。 完成作业以后,调用打印作业对象的end()方法。这表明打印作业已经完成,并允许打印假脱机系统运行作业,以及释放打印机以供其他作业使用。 练 习 练习目标-在这个练习中,你将创建一个使用许多组件的复杂应用程序。 一、准备 为了很好地完成这个练习,你必须理解AWT的目的、它的事件处理器以及它的图形特性。 二、任务 水平1: 创建绘图程序布局 用java.awt包,创建一个Java应用程序――PaintGUI.java,它将添加显示在下图中的组件。 使用帮助菜单的对话框。 水平3:创建绘图程序 以上图中的布局为指导,创建一个简单的绘图程序。包含一个使用GUI的PaintHandler类;使用事件处理来完成这个目标。 选取打印菜单选项时,使用java.awt.PrintJob来打印创建的图形。 三、练习小结 讨论 - 花几分钟时间讨论一下,在本实验练习过程中你都经历、提出和发现了什么。 经验 解释 总结 应用 四、检查一下你的进度 在进入下一个模块的学习之前,请确认你能够: 认识关键的AWT组件 使用AWT组件来创建真实程序的用户界面 控制AWT组件使用的颜色和字体 使用Java打印机制 五、思考 怎样才能使AWT更好地工作?