11.5.2 事件和事件监听器

11.5.2 事件和事件监听器

这里有一张图片

从图11.20中可以看出,当外部动作在AWT组件上进行操作时,系统会自动生成事件对象,这个事件对象是EventObject子类的实例,该事件对象会触发注册到事件源上的事件监听器。

AWT事件机制涉及三个成员: 事件源、事件和事件监听器,其中事件源最容易创建,只要通过new来创建一个AWT组件,该组件就是事件源;事件是由系统自动产生的,无须程序员关心。所以,实现事件监听器是整个事件处理的核心

事件监听器必须实现事件监听器接口,AWT提供了大量的事件监听器接口用于实现不同类型的事件监听器,用于监听不同类型的事件。AWT中提供了丰富的事件类,用于封装不同组件上所发生的特定操作—AWT的事件类都是AWTEvent类的子类,AWTEventEventObject的子类。

EventObject类代表更广义的事件对象,包括Swing组件上所触发的事件、数据库连接所触发的事件等

AWT事件分类

AWT事件分为两大类:

低级事件和高级事件

1. 低级事件

低级事件是指基于特定动作的事件。比如进入、点击、拖放等动作的鼠标事件,当组件得到焦点、失去焦点时触发焦点事件

事件 描述
ComponentEvent 组件事件,当组件尺寸发生变化、位置发生移动、显示/隐藏状态发生改变时触发该事件。
ContainerEvent 容器事件,当容器里发生添加组件、删除组件时触发该事件。
WindowEvent 窗口事件,当窗口状态发生改变(如打开、关闭、最大化、最小化)时触发该事件。
FocusEvent 焦点事件,当组件得到焦点或失去焦点时触发该事件。
KeyEvent 键盘事件,当按键被按下、松开、单击时触发该事件。
MouseEvent 鼠标事件,当进行单击、按下、松开、移动鼠标等动作时触发该事件。
PaintEvent 组件绘制事件,该事件是一个特殊的事件类型,当GUI组件调用update/paint方法来呈现自身时触发该事件,PaintEvent事件并非专用于事件处理模型。

2. 高级事件(语义事件)

高级事件是基于语义的事件,它可以不和特定的动作相关联,而依赖于触发此事件的类。比如,在TextField中按Enter键会触发ActionEvent事件,在滑动条上移动滑块会触发AdjustmentEvent事件,选中项目列表的某一项就会触发ItemEvent事件。

事件 描述
ActionEvent 动作事件,当按钮、菜单项被单击,在TextField中按Enter键时触发该事件。
AdjustmentEvent 调节事件,在滑动条上移动滑块以调节数值时触发该事件。
ItemEvent 选项事件,当用户选中某项,或取消选中某项时触发该事件。
TextEvent 文本事件,当文本框、文本域里的文本发生改变时触发该事件

AWT事件继承层次图如图11.21所示

图片

图11.21中使用粗线框圈出的那些事件是常用的AWT事件。对于没有用粗线框圈出的事件,程序员很少使用它们,它们可能被作为事件基类或作为系统内部实现来使用。

不同的事件需要使用不同的监听器监听,不同的监听器需要实现不同的监听器接口,当指定事件发生后,事件监听器就会调用所包含的事件处理器(实例方法)来处理事件。

表11.1 事件、监听器接口和处理器之间的对应关系

事件 监听器接口 处理器和触发时机
ActionEvent ActionListener actionPerformed
AdjustmentEvent AdjustmentListener adjustmentValueChanged
ContainerEvent Containerlistener
  • componentAdded: 向容器中添加组件时触发
  • componentRemoved: 从容器中删除组件时触发
FocusEvent FocusListener
  • focusGained: 组件得到焦点时触发
  • focusLost: 组件失去焦点时触发
ComponentEvent ComponentListener
  • componentHidden: 组件被隐藏时触发
  • componentMoved: 组件位置发生改变时触发
  • componentResized: 组件大小发生改变时触发
  • componentShown: 组件被显示时触发
KeyEvent KeyListener
  • keyPressed: 按下某个按键时触发
  • keyReleased: 松开某个按键时触发
  • keyTyped: 单击某个按键时触发
MouseEvent MouseListener
  • mouseClicked: 在某个组件上单击鼠标键时触发
  • mouseEntered: 鼠标进入某个组件时触发
  • mouseExited: 鼠标离开某个组件时触发
  • mousePressed: 在某个组件上按下鼠标键时触发
  • mouseReleased: 在某个组件上松开鼠标键时触发
MouseEvent MouseMotionListener
  • mouseDragged: 在某个组件上移动鼠标,且按下鼠标键时触发
  • mouseMoved: 在某个组件上移动鼠标,且没有按下鼠标键时触发
TextEvent TextListener textValueChanged: 文本组件里的文本发生改变时触发
ItemEvent ItemListener itemStateChanged: 某项被选中或取消选中时触发
WindowEvent WindowListener
  • windowActivated: 窗口被激活时触发
  • windowClosed: 窗口调用dispose()即将关闭时触发
  • windowClosing: 用户单击窗口右上角的“x”按钮时触发
  • windowDeactivated: 窗口失去激活时触发
  • windowDeiconified: 窗口被恢复时触发
  • windowIconified:窗口最小化时触发
  • windowOpened: 窗口首次被打开时触发

通过表11.1可以大致知道常用组件可能发生哪些事件,以及该事件对应的监听器接口,通过实现该监听器接口就可以实现对应的事件处理器,然后通过addXxxlistener()方法将事件监听器注册给指定的组件(事件源)。当事件源组件上发生特定事件时,被注册到该组件的事件监听器里的对应方法(事件处理器)将被触发。

ActionListenerAdjustmentListener等事件监听器接口只包含一个抽象方法,这种接口也就是前面介绍的函数式接口,因此可用Lambda表达式来创建监听器对象

事件处理中的事件处理器方法由系统负责调用

实际上,可以如下理解事件处理模型: 当事件源组件上发生事件时,系统将会执行该事件源组件的所有监听器里的对应方法。与前面编程方式不同的是,普通Java程序里的方法由程序主动调用,事件处理中的事件处理器方法由系统负责调用

程序示例 一个监听器监听多个组件

下面程序示范了一个监听器监听多个组件,一个组件被多个监听器监听的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.awt.*;
import java.awt.event.*;

public class MultiListener {
private Frame f = new Frame("测试");
private TextArea ta = new TextArea(6, 40);
private Button b1 = new Button("按钮一");
private Button b2 = new Button("按钮二");

public void init() {
// 创建FirstListener监听器的实例
FirstListener fl = new FirstListener();
// 给b1按钮注册两个事件监听器
b1.addActionListener(fl);
b1.addActionListener(new SecondListener());
// 将f1事件监听器注册给b2按钮
b2.addActionListener(fl);
f.add(ta);
Panel p = new Panel();
p.add(b1);
p.add(b2);
f.add(p, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}

class FirstListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
ta.append("第一个事件监听器被触发,事件源是:" + e.getActionCommand() + "\n");
}
}

class SecondListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
ta.append("单击了“" + e.getActionCommand() + "”按钮\n");
}
}

public static void main(String[] args) {
new MultiListener().init();
}
}

上面程序中b1按钮增加了两个事件监听器,当用户单击b1按钮时,两个监听器的actionPerform()方法都会被触发;而且f1监听器同时监听b1b2两个按钮,当b1b2任意一个按钮被单击时,fl监听器的actionPerform()方法都会被触发。

上面程序中调用了ActionEvent对象的getActionCommand()方法,用于获取被单击按钮上的文本。

运行上面程序,分别单击“按钮一”、“按钮二”一次,将看到如图11.2所示的窗口效果

这里有一张图片

程序示例 窗口监听器 可关闭窗体

下面程序为窗口添加窗口监听器,从而示范窗口监听器的用法,并允许用户单击窗口右上角的“×”按钮来结束程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import java.awt.*;
import java.awt.event.*;

public class WindowListenerTest {
private Frame f = new Frame("测试");
private TextArea ta = new TextArea(6, 40);

public void init() {
// 为窗口添加窗口事件监听器
f.addWindowListener(new MyListener());
f.add(ta);
f.pack();
f.setVisible(true);
}

// 实现一个窗口监听器类
class MyListener implements WindowListener {
public void windowActivated(WindowEvent e) {
ta.append("窗口被激活!\n");
}

public void windowClosed(WindowEvent e) {
ta.append("窗口被成功关闭!\n");
}

public void windowClosing(WindowEvent e) {
ta.append("用户关闭窗口!\n");
System.exit(0);
}

public void windowDeactivated(WindowEvent e) {
ta.append("窗口失去焦点!\n");
}

public void windowDeiconified(WindowEvent e) {
ta.append("窗口被恢复!\n");
}

public void windowIconified(WindowEvent e) {
ta.append("窗口被最小化!\n");
}

public void windowOpened(WindowEvent e) {
ta.append("窗口初次被打开!\n");
}
}

public static void main(String[] args) {
new WindowListenerTest().init();
}
}

上面程序详细监听了窗口的每个动作,当用户单击窗口右上角的每个按钮时,程序都会做出相应的响应,当用户单击窗口中的关闭按钮“×”时,程序将正常退出。

事件适配器

大部分时候,程序无须监听窗口的每个动作,只需要为用户单击窗口中的关闭按钮“×”提供响应即可。无须为每个窗口事件提供响应,程序只想重写windowClosing事件处理器,但因为该监听器实现了WindowListener接口,实现该接口就不得不实现该接口里的每个抽象方法,这是非常烦琐的事情。为此,AWT提供了事件适配器