5.2 AWT事件处理模型
上一节中的主要内容是如何放置各种组件,使图形界面更加丰富多彩,但是还不能响应用户的任何操作,要能够让图形界面接收用户的操作,就必须给各个组件加上事件处理机制。在事件处理的过程中,主要涉及三类对象: ◇ Event-事件,用户对界面操作在java语言上的描述,以类的形式出现,例如键盘操作对应的事件类是KeyEvent。 ◇ Event Source-事件源,事件发生的场所,通常就是各个组件,例如按钮Button。 ◇ Event handler-事件处理者,接收事件对象并对其进行处理的对象 例如,如果用户用鼠标单击了按钮对象button,则该按钮button就是事件源,而java运行时系统会生成ActionEvent类的对象actionE,该对象中描述了该单击事件发生时的一些信息,然后,事件处理者对象将接收由java运行时系统传递过来的事件对象actionE并进行相应的处理。 由于同一个事件源上可能发生多种事件,因此java采取了授权处理机制(Delegation Model),事件源可以把在其自身所有可能发生的事件分别授权给不同的事件处理者来处理。比如在Canvas对象上既可能发生鼠标事件,也可能发生键盘事件,该Canvas对象就可以授权给事件处理者一来处理鼠标事件,同时授权给事件处理者二来处理键盘事件。有时也将事件处理者称为监听器,主要原因也在于监听器时刻监听着事件源上所有发生的事件类型,一旦该事件类型与自己所负责处理的事件类型一致,就马上进行处理。授权模型把事件的处理委托给外部的处理实体进行处理,实现了将事件源和监听器分开的机制。事件处理者(监听器)通常是一个类,该类如果要能够处理某种类型的事件,就必须实现与该事件类型相对的接口。例如例5.9中类ButtonHandler之所以能够处理ActionEvent事件,原因在于它实现了与ActionEvent事件对应的接口ActionListener。每个事件类都有一个与之相对应的接口。 将事件源对象和事件处理器(事件监听器)分开。
例5.9 import java.awt.*; import java.awt.event.*; public class TestButton { public static void main(String args[]) { Frame f = new Frame("Test"); Button b = new Button("Press Me!"); b.addActionListener(new ButtonHandler()); /*注册监听器进行授权,该方法的参数是事件处理者对象,要处理的事件类型可以从方法名中看出,例如本方法要授权处理的是ActionEvent,因为方法名是addActionListener。*/ f.setLayout(new FlowLayout()); //设置布局管理器 f.add(b); f.setSize(200,100); f.setVisible(true); } } class ButtonHandler implements ActionListener { //实现接口ActionListener才能做事件ActionEvent的处理者 public void actionPerformed(ActionEvent e) //系统产生的ActionEvent事件对象被当作参数传递给该方法 { System.out.println("Action occurred"); //本接口只有一个方法,因此事件发生时,系统会自动调用本方法,需要做的操作就把代码写在则个方法里。 } } 使用授权处理模型进行事件处理的一般方法归纳如下: 1.对于某种类型的事件XXXEvent, 要想接收并处理这类事件,必须定义相应的事件监听器类,该类需要实现与该事件相对应的接口XXXListener; 2.事件源实例化以后,必须进行授权,注册该类事件的监听器,使用addXXXListener(XXXListener ) 方法来注册监听器。 5.2.1事件类 与AWT有关的所有事件类都由java.awt.AWTEvent类派生,它也是EventObject类的子类。AWT事件共有10类,可以归为两大类:低级事件和高级事件。 java.util.EventObject类是所有事件对象的基础父类,所有事件都是由它派生出来的。AWT的相关事件继承于java.awt.AWTEvent类,这些AWT事件分为两大类:低级事件和高级事件,低级事件是指基于组件和容器的事件,当一个组件上发生事件,如:鼠标的进入,点击,拖放等,或组件的窗口开关等,触发了组件事件。高级事件是基于语义的事件,它可以不和特定的动作相关联,而依赖于触发此事件的类,如在TextField中按Enter键会触发ActionEvent事件,滑动滚动条会触发AdjustmentEvent事件,或是选中项目列表的某一条就会触发ItemEvent事件。 ◇ 低级事件 ComponentEvent( 组件事件:组件尺寸的变化,移动) ContainerEvent( 容器事件:组件增加,移动) WindowEvent( 窗口事件:关闭窗口,窗口闭合,图标化) FocusEvent( 焦点事件:焦点的获得和丢失) KeyEvent( 键盘事件:键按下、释放) MouseEvent( 鼠标事件:鼠标单击,移动) ◇ 高级事件(语义事件) ActionEvent(动作事件:按钮按下,TextField中按Enter键) AdjustmentEvent(调节事件:在滚动条上移动滑块以调节数值) ItemEvent(项目事件:选择项目,不选择"项目改变") TextEvent(文本事件,文本对象改变) 5.2.2事件监听器
例如窗口事件接口: public interface WindowListener extends EventListener{ public void windowClosing(WindowEvent e); //把退出窗口的语句写在本方法中 public void windowOpened(WindowEvent e); //窗口打开时调用 public void windowIconified(WindowEvent e); //窗口图标化时调用 public void windowDeiconified(WindowEvent e); //窗口非图标化时调用 public void windowClosed(WindowEvent e); //窗口关闭时调用 public void windowActivated(WindowEvent e); //窗口激活时调用 public void windowDeactivated(WindowEvent e); //窗口非激活时调用 } AWT的组件类中提供注册和注销监听器的方法: ◇ 注册监听器: public void add (listener); 例如Button类:(查API) public class Button extends Component { …… public synchronized void addActionListener(ActionListener l); ……} 5.2.3 AWT事件及其相应的监听器接口(1) 表5.1列出了所有AWT事件及其相应的监听器接口,一共10类事件,11个接口。下面这张表应能牢牢记住。 表5.1
事件类别 |
描述信息 |
接口名 |
方法 |
ActionEvent |
激活组件 |
ActionListener |
actionPerformed(ActionEvent) |
ItemEvent |
选择了某些项目 |
ItemListener |
itemStateChanged(ItemEvent) |
MouseEvent |
鼠标移动 |
MouseMotionListener |
mouseDragged(MouseEvent) mouseMoved(MouseEvent) |
鼠标点击等 |
MouseListener |
mousePressed(MouseEvent) mouseReleased(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mouseClicked(MouseEvent) |
KeyEvent |
键盘输入 |
KeyListener |
keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent) |
FocusEvent |
组件收到或失去焦点 |
FocusListener |
focusGained(FocusEvent) focusLost(FocusEvent) |
AdjustmentEvent |
移动了滚动条等组件 |
AdjustmentListener |
adjustmentValueChanged(AdjustmentEvent) |
ComponentEvent |
对象移动缩放显示隐藏等 |
ComponentListener |
componentMoved(ComponentEvent) componentHidden(ComponentEvent) componentResized(ComponentEvent) componentShown(ComponentEvent) |
WindowEvent |
窗口收到窗口级事件 |
WindowListener |
windowClosing(WindowEvent) windowOpened(WindowEvent) windowIconified(WindowEvent) windowDeiconified(WindowEvent) windowClosed(WindowEvent) windowActivated(WindowEvent) windowDeactivated(WindowEvent) |
ContainerEvent |
容器中增加删除了组件 |
ContainerListener |
componentAdded(ContainerEvent) componentRemoved(ContainerEvent) |
TextEvent |
文本字段或文本区发生改变 |
TextListener |
textValueChanged(TextEvent) |
5.2.4事件适配器
Java语言为一些Listener接口提供了适配器(Adapter)类。可以通过继承事件所对应的Adapter类,重写需要方法,无关方法不用实现。事件适配器为我们提供了一种简单的实现监听器的手段, 可以缩短程序代码。但是,由于java的单一继承机制,当需要多种监听器或此类已有父类时,就无法采用事件适配器了。 1.事件适配器--EventAdapter 下例中采用了鼠标适配器: import java.awt.*; import java.awt.event.*; public class MouseClickHandler extends MouseAdaper{ public void mouseClicked(MouseEvent e) //只实现需要的方法 { ……} } java.awt.event包中定义的事件适配器类包括以下几个: 1.ComponentAdapter( 组件适配器) 2.ContainerAdapter( 容器适配器) 3.FocusAdapter( 焦点适配器) 4.KeyAdapter( 键盘适配器) 5.MouseAdapter( 鼠标适配器) 6.MouseMotionAdapter( 鼠标运动适配器) 7.WindowAdapter( 窗口适配器) 2. 用内部类实现事件处理 内部类(inner class)是被定义于另一个类中的类,使用内部类的主要原因是由于: ◇ 一个内部类的对象可访问外部类的成员方法和变量,包括私有的成员。 ◇ 实现事件监听器时,采用内部类、匿名类编程非常容易实现其功能。 ◇ 编写事件驱动程序,内部类很方便。 因此内部类所能够应用的地方往往是在AWT的事件处理机制中。 例5.11 import java.awt.* ; import java.awt.event.*; public class InnerClass{ private Frame f; private TextField tf; public InnerClass(){ f=new Frame("Inner classes example"); tf=new TextField(30); } public voidi launchFrame(){ Label label=new Label("Click and drag the mouse"); f.add(label,BorderLayout.NORTH); f.add(tf,BorderLayout.SOUTH); f.addMouseMotionListener(new MyMouseMotionListener());/*参数为内部类对象*/ f.setSize(300,200); f.setVisible(true); } class MyMouseMotionListener extends MouseMotionAdapter{/*内部类开始*/ public void mouseDragged(MouseEvent e) { String s="Mouse dragging: x="+e.getX()+"Y="+e.getY(); tf.setText(s); } } ; public static void main(String args[]) { InnerClass obj=new InnerClass(); obj.launchFrame(); } }//内部类结束 } 3.匿名类(Anonymous Class) 当一个内部类的类声名只是在创建此类对象时用了一次,而且要产生的新类需继承于一个已有的父类或实现一个接口,才能考虑用匿名类,由于匿名类本身无名,因此它也就不存在构造方法,它需要显示地调用一个无参的父类的构造方法,并且重写父类的方法。所谓的匿名就是该类连名字都没有,只是显示地调用一个无参的父类的构造方法。 例5.12 import java.awt.* ; import java.awt.event.*; public class AnonymousClass{ private Frame f; private TextField tf; public AnonymousClass(){ f=new Frame("Inner classes example"); tf=new TextField(30); } public void launchFrame(){ Label label=new Label("Click and drag the mouse"); f.add(label,BorderLayout.NORTH); f.add(tf,BorderLayout.SOUTH); f.addMouseMotionListener(new MouseMotionAdapter(){ //匿名类开始 public void mouseDragged(MouseEvent e){ String s="Mouse dragging: x="+e.getX()+"Y="+e.getY(); tf.setText(s); } } ); //匿名类结束 f.setSize(300,200); f.setVisible(true); } public static void main(String args[]) { AnonymousClass obj=new AnonymousClass(); obj.launchFrame(); } } 其实大家仔细分析一下,例5.11和5.12实现的都是完全一样的功能,只不过采取的方式不同。5.11中的事件处理类是一个内部类,而5.12的事件处理类是匿名类,可以说从类的关系来说是越来越不清楚,但是程序也越来越简练。熟悉这两种方式也十分有助于大家编写图形界面的程序。 5.3 AWT组件库(1) 本节从应用的角度进一步介绍AWT的一些组件,目的使大家加深对AWT的理解,掌握如何用各种组件构造图形化用户界面,学会控制组件的颜色和字体。下面是一些常用的组件的介绍: 1. 按钮(Button) 按钮是最常用的一个组件,其构造方法是:Button b = new Button("Quit"); 当按钮被点击后,会产生ActionEvent事件,需ActionListener接口进行监听和处理事件。 ActionEvent的对象调用getActionCommand()方法可以得到按钮的标识名,缺省按钮名为label。 用setActionCommand()可以为按钮设置组件标识符。 2.复选框 (Checkbox) 复选框提供简单的"on/off"开关,旁边显示文本标签。
构造方法如下: setLayout(new GridLayout(3,1)); add(new Checkbox("one",null,true)); add(new Checkbox("two")); add(new Checkbox("three")); 3.复选框组(CheckboxGroup) 使用复选框组,可以实现单选框的功能。方法如下: setLayout(new GridLayout(3, 1)); CheckboxGroup cbg = new CheckboxGroup(); add(new Checkbox("one", cbg, true)); add(new Checkbox("two", cbg, false)); add(new Checkbox("three", cbg, false)); 4. 下拉式菜单(Choice) 下拉式菜单每次只能选择其中的一项,它能够节省显示空间,适用于大量选项。 Choice Colorchooser=new Choice(); Colorchooser.add("Green"); Colorchooser.add("Red"); Colorchooser.add("Blue"); Choice 用ItemListener接口来进行监听 5. 单行文本输入区(TextField) 只能显示一行,当回车键被按下时,会发生ActionEvent事件,可以通过ActionListener中的actionPerformed()方法对事件进行相应处理。可以使用setEditable(boolean)方法设置为只读属性。 单行文本输入区构造方法如下: TextField tf1,tf2,tf3,tf4: tf1=new TextField(); tf2=new TextField("",20); //显示区域为20列 tf3=new TextField("Hello!"); //按文本区域大小显示 tf4=new TextField("Hello!",30); //初始文本为Hello!, 显示区域为30列 7. 文本输入区(TextArea) TextArea可以显示多行多列的文本。使用setEditable(boolean)方法,可以将其设置为只读的。在TextArea中可以显示水平或垂直的滚动条。 要判断文本是否输入完毕,可以在TextArea旁边设置一个按钮,通过按钮点击产生的ActionEvent对输入的文本进行处理。 |