ITEEDU

13.16.2 事件和接收者类型

所有AWT组件都被改变成包含addXXXListener()和removeXXXListener()方法,因此特定的接收器类型可从每个组件中增加和删除。我们会注意到“XXX”在每个场合中同样表示自变量的方法,例如,addFooListener(FooListener fl)。下面这张表格总结了通过提供addXXXListener()和removeXXXListener()方法,从而支持那些特定事件的相关事件、接收器、方法以及组件。

事件,接收器接口及添加和删除方法 支持这个事件的组件

Event, listener interface and add- and remove-methods Components supporting this event
ActionEvent ActionListener addActionListener( ) removeActionListener( ) Button, List, TextField, MenuItem, and its derivatives including CheckboxMenuItem, Menu, and PopupMenu
AdjustmentEvent AdjustmentListener addAdjustmentListener( ) removeAdjustmentListener( ) Scrollbar Anything you create that implements the Adjustable interface
ComponentEvent ComponentListener addComponentListener( ) removeComponentListener( ) Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField
ContainerEvent ContainerListener addContainerListener( ) removeContainerListener( ) Container and its derivatives, including Panel, Applet, ScrollPane, Window, Dialog, FileDialog, and Frame
FocusEvent FocusListener addFocusListener( ) removeFocusListener( ) Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame Label, List, Scrollbar, TextArea, and TextField
KeyEvent KeyListener addKeyListener( ) removeKeyListener( ) Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField
MouseEvent (for both clicks and motion) MouseListener addMouseListener( ) removeMouseListener( ) Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField
MouseEvent[55] (for both clicks and motion) MouseMotionListener addMouseMotionListener( ) removeMouseMotionListener( ) Component and its derivatives, including Button, Canvas, Checkbox, Choice, Container, Panel, Applet, ScrollPane, Window, Dialog, FileDialog, Frame, Label, List, Scrollbar, TextArea, and TextField
WindowEvent WindowListener addWindowListener( ) removeWindowListener( ) Window and its derivatives, including Dialog, FileDialog, and Frame
ItemEvent ItemListener addItemListener( ) removeItemListener( ) Checkbox, CheckboxMenuItem, Choice, List, and anything that implements the ItemSelectable interface
TextEvent TextListener addTextListener( ) removeTextListener( ) Anything derived from TextComponent, including TextArea and TextField

⑤:即使表面上如此,但实际上并没有MouseMotiionEvent(鼠标运动事件)。单击和运动都合成到MouseEvent里,所以MouseEvent在表格中的这种另类行为并非一个错误。

可以看到,每种类型的组件只为特定类型的事件提供了支持。这有助于我们发现由每种组件支持的事件,如下表所示:

组件类型 支持的事件

Component type Events supported by this component
Adjustable AdjustmentEvent
Applet ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Button ActionEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Canvas FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Checkbox ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
CheckboxMenuItem ActionEvent, ItemEvent
Choice ItemEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Component FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Container ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Dialog ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
FileDialog ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Frame ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Label FocusEvent, KeyEvent, MouseEvent, ComponentEvent
List ActionEvent, FocusEvent, KeyEvent, MouseEvent, ItemEvent, ComponentEvent
Menu ActionEvent
MenuItem ActionEvent
Panel ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
PopupMenu ActionEvent
Scrollbar AdjustmentEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
ScrollPane ContainerEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
TextArea TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
TextComponent TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
TextField ActionEvent, TextEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent
Window ContainerEvent, WindowEvent, FocusEvent, KeyEvent, MouseEvent, ComponentEvent

一旦知道了一个特定的组件支持哪些事件,就不必再去寻找任何东西来响应那个事件。只需简单地:

  • (1) 取得事件类的名字,并删掉其中的“Event”字样。在剩下的部分加入“Listener”字样。这就是在我们的内部类里需要实现的接收器接口。
  • (2) 实现上面的接口,针对想要捕获的事件编写方法代码。例如,假设我们想捕获鼠标的移动,所以需要为MouseMotiionListener接口的mouseMoved()方法编写代(当然还必须实现其他一些方法,但这里有捷径可循,马上就会讲到这个问题)。
  • (3) 为步骤2中的接收器类创建一个对象。随自己的组件和方法完成对它的注册,方法是在接收器的名字里加入一个前缀“add”。比如addMouseMotionListener()。

下表是对接收器接口的一个总结:

接收器接口 接口中的方法

Listener interface w/ adapter Methods in interface
ActionListener actionPerformed(ActionEvent)
AdjustmentListener adjustmentValueChanged( AdjustmentEvent)
ComponentListener ComponentAdapter componentHidden(ComponentEvent) componentShown(ComponentEvent) componentMoved(ComponentEvent) componentResized(ComponentEvent)
ContainerListener ContainerAdapter componentAdded(ContainerEvent) componentRemoved(ContainerEvent)
FocusListener FocusAdapter focusGained(FocusEvent) focusLost(FocusEvent)
KeyListener KeyAdapter keyPressed(KeyEvent) keyReleased(KeyEvent) keyTyped(KeyEvent)
MouseListener MouseAdapter mouseClicked(MouseEvent) mouseEntered(MouseEvent) mouseExited(MouseEvent) mousePressed(MouseEvent) mouseReleased(MouseEvent)
MouseMotionListener MouseMotionAdapter mouseDragged(MouseEvent) mouseMoved(MouseEvent)
WindowListener WindowAdapter windowOpened(WindowEvent) windowClosing(WindowEvent) windowClosed(WindowEvent) windowActivated(WindowEvent) windowDeactivated(WindowEvent) windowIconified(WindowEvent) windowDeiconified(WindowEvent)
ItemListener itemStateChanged(ItemEvent)
TextListener textValueChanged(TextEvent)

1. 用接收器适配器简化操作

在上面的表格中,我们可以注意到一些接收器接口只有唯一的一个方法。它们的执行是无轻重的,因为我们仅当需要书写特殊方法时才会执行它们。然而,接收器接口拥有多个方法,使用起来却不太友好。例如,我们必须一直运行某些事物,当我们创建一个应用程序时对帧提供一个WindowListener,以便当我们得到windowClosing()事件时可以调用System.exit(0)以退出应用程序。但因为WindowListener是一个接口,我们必须执行其它所有的方法即使它们不运行任何事件。这真令人讨厌。

为了解决这个问题,每个拥有超过一个方法的接收器接口都可拥有适配器,它们的名我们可以在上面的表格中看到。每个适配器为每个接口方法提供默认的方法。(WindowAdapter的默认方法不是windowClosing(),而是System.exit(0)方法。)此外我们所要做的就是从适配器处继承并过载唯一的需要变更的方法。例如,典型的WindowListener我们会像下面这样的使用。

class MyWindowListener extends WindowAdapter {
  public void windowClosing(WindowEvent e) {
    System.exit(0);
  }
}

适配器的全部宗旨就是使接收器的创建变得更加简便。

但所谓的“适配器”也有一个缺点,而且较难发觉。假定我们象上面那样写一个WindowAdapter:

class MyWindowListener extends WindowAdapter {
  public void WindowClosing(WindowEvent e) {
    System.exit(0);
  }
}

表面上一切正常,但实际没有任何效果。每个事件的编译和运行都很正常——只是关闭窗口不会退出程序。您注意到问题在哪里吗?在方法的名字里:是WindowClosing(),而不是windowClosing()。大小写的一个简单失误就会造成一个崭新的方法。但是,这并非我们关闭窗口时调用的方法,所以当然没有任何效果。