上一节中的主要内容是如何放置各种组件,使图形界面更加丰富多彩,但是还不能响应用户的任何操作,要能够让图形界面接收用户的操作,就必须给各个组件加上事件处理机制。在事件处理的过程中,主要涉及三类对象:
◇ Event-事件,用户对界面操作在java语言上的描述,以类的形式出现,例如键盘操作对应的事件类是KeyEvent。
◇ Event Source-事件源,事件发生的场所,通常就是各个组件,例如按钮Button。
◇ Event handler-事件处理者,接收事件对象并对其进行处理的对象。
例如,如果用户用鼠标单击了按钮对象button,则该按钮button就是事件源,而java运行时系统会生成ActionEvent类的对象actionE,该对象中描述了该单击事件发生时的一些信息,然后,事件处理者对象将接收由java运行时系统传递过来的事件对象actionE并进行相应的处理。
由于同一个事件源上可能发生多种事件,因此java采取了授权处理机制(Delegation Model),事件源可以把在其自身所有可能发生的事件分别授权给不同的事件处理者来处理。比如在Canvas对象上既可能发生鼠标事件,也可能发生键盘事件,该Canvas对象就可以授权给事件处理者一来处理鼠标事件,同时授权给事件处理者二来处理键盘事件。有时也将事件处理者称为xxx,主要原因也在于xxx时刻监听着事件源上所有发生的事件类型,一旦该事件类型与自己所负责处理的事件类型一致,就马上进行处理。授权模型把事件的处理委托给外部的处理实体进行处理,实现了将事件源和xxx分开的机制。事件处理者(xxx)通常是一个类,该类如果要能够处理某种类型的事件,就必须实现与该事件类型相对的接口。例如例5.9中类ButtonHandler之所以能够处理ActionEvent事件,原因在于它实现了与ActionEvent事件对应的接口ActionListener。每个事件类都有一个与之相对应的接口。
将事件源对象和事件处理器(事件xxx)分开。如图5.2所示
打个不太恰当的比喻,比如说有一位李先生,李先生可能会发生很多法律纠纷,可能是民事法律纠纷,也可能是刑事法律纠纷,那么李先生可以请律师,他可以授权王律师负责帮他打民事法律的官司,同时也可以授权张律师帮他打刑事法律的官司。这个请律师的过程从李先生的角度来看,就是授权的过程,而从王律师和张律师的角度来看,一旦被授权,他们就得时刻对李先生负责,"监听"着李先生,一旦发生民事纠纷了,王律师就要马上去处理,而一旦发生刑事纠纷了,张律师就要马上进行处理。此时此刻,李先生就是事件源,王律师是一个事件处理者,张律师是另外一个事件处理者,民事纠纷和刑事纠纷就是不同类型的事件。
例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()); /*注册xxx进行授权,该方法的参数是事件处理者对象,要处理的事件类型可以从方法名中看出,例如本方法要授权处理的是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, 要想接收并处理这类事件,必须定义相应的事件xxx类,该类需要实现与该事件相对应的接口XXXListener;
2.事件源实例化以后,必须进行授权,注册该类事件的xxx,使用addXXXListener(XXXListener ) 方法来注册xxx。
事件类
与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(文本事件,文本对象改变)
事件xxx
每类事件都有对应的事件xxx,xxx是接口,根据动作来定义方法。
例如,与键盘事件KeyEvent相对应的接口是:
public interface KeyListener extends EventListener {
public void keyPressed(KeyEvent ev);
public void keyReleased(KeyEvent ev);
public void keyTyped(KeyEvent ev);
}
注意到在本接口中有三个方法,那么java运行时系统何时调用哪个方法?其实根据这三个方法的方法名就能够知道应该是什么时候调用哪个方法执行了。当键盘刚按下去时,将调用keyPressed( )方法执行,当键盘抬起来时,将调用keyReleased( )方法执行,当键盘敲击一次时,将调用keyTyped( )方法执行。
又例如窗口事件接口:
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);
//窗口xx时调用
public void windowDeactivated(WindowEvent e);
//窗口非xx时调用
}
AWT的组件类中提供注册和注销xxx的方法:
◇ 注册xxx:
public void add<ListenerType> (<ListenerType>listener);
◇ 注销xxx:
public void remove<ListenerType> (<ListenerType>listener);
例如Button类:(查API)
public class Button extends Component {
……
public synchronized void addActionListener(ActionListener l);
public synchronized void removeActionListener(ActionListener l);
……}
AWT事件及其相应的xxx接口(1)
表5.1列出了所有AWT事件及其相应的xxx接口,一共10类事件,11个接口。下面这张表应能牢牢记住。
表5.1
|
|
|
|
ActionEvent |
xx组件 |
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) |
AWT事件及其相应的xxx接口(2)
例5.10说明事件处理模型的应用。
例5.10
import java.awt.*;
import java.awt.event.*;
public class ThreeListener implements MouseMotionListener,MouseListener,WindowListener {
//实现了三个接口
private Frame f;
private TextField tf;
public static void main(String args[])
{
ThreeListener two = new ThreeListener();
two.go(); }
public void go() {
f = new Frame("Three listeners example");
f.add(new Label("Click and drag the mouse"),"North");
tf = new TextField(30);
f.add(tf,"South"); //使用缺省的布局管理器
f.addMouseMotionListener(this); //注册xxxMouseMotionListener
f.addMouseListener(this); //注册xxxMouseListener
f.addWindowListener(this); //注册xxxWindowListener
f.setSize(300,200);
f.setVisible(true);
}
public void mouseDragged (MouseEvent e) {
//实现mouseDragged方法
String s = "Mouse dragging : X="+e.getX()+"Y = "+e.getY();
tf.setText(s);
}
public void mouseMoved(MouseEvent e){}
//对其不感兴趣的方法可以方法体为空
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){
String s = "The mouse entered";
tf.setText(s);
}
public void mouseExited(MouseEvent e){
String s = "The mouse has left the building";
tf.setText(s);
}
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){ }
public void windowClosing(WindowEvent e) {
//为了使窗口能正常关闭,程序正常退出,需要实现windowClosing方法
System.exit(1);
}
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) {}
}
上例中有如下几个特点:
1.可以声明多个接口,接口之间用逗号隔开。
……implements MouseMotionListener, MouseListener, WindowListener;
2.可以由同一个对象监听一个事件源上发生的多种事件:
f.addMouseMotionListener(this);
f.addMouseListener(this);
f.addWindowListener(this);
则对象f 上发生的多个事件都将被同一个xxx接收和处理。
3.事件处理者和事件源处在同一个类中。本例中事件源是Frame f,事件处理者是类ThreeListener,其中事件源Frame f是类ThreeListener的成员变量。
4.可以通过事件对象获得详细资料,比如本例中就通过事件对象获得了鼠标发生时的坐标值。
public void mouseDragged(MouseEvent e) {
String s="Mouse dragging :X="+e.getX()+"Y="+e.getY();
tf.setText(s);
}