摘 要: 本文在Java 1.1的事件处理委托模型的基础上,得出了创建自己Listener的一般方法,并通过例程给出了不同情况下的Listener创建的具体实现。
关键词: 事件处理,委托模型,事件源,监听器
一、构造Listener的基本要素
Java1.1中最显著的特色便是引入了采用listener机制的事件委托模型。在实际的开发过程中,仅靠系统提供的listener有时并不能满足用户的需求。就此,本文给出了订制自己Listener的一般方法。
在的事件委托模型中,一个对象中发生的事件自己一般不作处理,而是委托给外部实体处理。各个对象之间发送事件实现互相通讯,发送事件的对象称事件源,接受事件的对象称监听器(listener)。发送事件的方式有事件的单目广播和多目广播。
事件源是产生事件的构件,java中所有AWT构件均为事件源。监听器是接收和处理事件的构件,它通过向事件源注册对XxxEvent类型事件可以实现对事件源中发生的事件的注册。
接口是java中很有意思的一个概念,它大致的规划处多个类共有行为,而把对这些行为的特殊实现留给类本身。接口中定义了一些抽象方法。一个XxxEvent的接口,可以将XxxEvent监听器和XxxEvent 事件源联系起来。
二、建立自己的Listener的一般方法
1)建立自己的事件类,用于封装要向监听器发送的数据。如class MyEvent{…}
2)预定义一个接口,规定要发送的事件以及对此事件的不同情况进行的操作。
如果事件源所发送的事件是在java.awt.event 包中定义的事件类,则监听器可以使用系统提供的Listener接口实现对事件源的监听,可跳过以上两步。
3)构造一个能引发某类型事件的事件源。事件源中必须规定监听器的注册、注销方法和发送事件的方法。例如在MyEvent事件源中有,
public void addMyEventListener(MyEventListener l){ … }
public void removeMyEventListener(MyEventListener l){ … }
这里, addMyEventListener方法是把MyEvent监听器的一个引用添加到listener中,而removeMyEventListener则是从listener中删除一个MyEvent监听器的引用。
当满足了引发事件的条件时,将引发事件向监听器发送,在发送事件的方法应有向监听器发送事件的语句。形如 listener.action1Happened(e);
4)构造监听器
任何类,只要它实现了MyEventListener接口成为一个MyEvent事件监听器,就可以监听MyEventSource类构件中的MyEvent事件。它必须对接口方法做出具体实现。
5)监听器向事件源的注册
构造了事件源、监听器后,要使监听器听到事件源中的事件,监听器还必须首先使用事件源中所规定的方法向事件源注册,形如,
source.addMyEventListener(processer); //processer向source注册
三、几种常见的定制listener的方法
1)使用系统接口接收来自非标准构件的系统事件
很多情况下,用户要根据自己的需要制作一些特殊的构件。由于系统提供了丰富的事件类型,通常,可以让自己构件的特定动作引发一个系统的事件,发送到已注册的监听器。这样,就可以直接使用系统提供的事件接口,实现对自制构件的监听,利用AWTEventMulticaster类方便地实现事件的单目、多目广播。下例的自制图形按钮支持事件的多目广播。凡是实现了ActionListener接口的构件,通过向该按钮的注册,均可监听其动作。
public class MyImageButton extends Canvas {
ActionListener actionListener=null;
……
public void addActionListener(ActionListener l) {//规定注册方法
actionListener = AWTEventMulticaster.add(actionListener, l);
}
public void removeActionListener(ActionListener l) {//规定注销方法
actionListener = AWTEventMulticaster.remove(actionListener, l);
}
public void processMouseEvent(MouseEvent event){//引发事件
…… ……
if (actionListener != null) {//鼠标放开时发送ActionEvent事件
ActionEvent evt=new actionEvent(this,ActionEvent.ACTION_PERFORMED,name);
actionListener.actionPerformed(evt);
}
}
}
2)使用自定义接口定制单目广播的Listener
有些情形,我们需要把一个类实例若干状态的改变,及时地通知另外的类实例,以便它们及时的做出响应。这时,就需要自定义一个接口,规范数据发送和接收双方的行为,保证数据的正确传递与处理。要发送的数据可以封装到一个事件类中发送,也可以按参量表的形式发送。事实上,也就是建立一个自己的Listener。例中Speedbar 类可以向已注册的类传递参量。Speedbar类实例把其中鼠标的位置转换为一个整形值发送已向其注册的MainPanel类实例中,让其处理。该类只支持单目广播。
public interface SpeedListener{
public void getSpeedValue(int value);
}
public class Speedbar extends Canvas {
protected SpeedListener speedListener=null;
……
public void addSpeedListener(SpeedListener l) { speedListener = l;}//规//定注册方法
public void removeSpeedListener(SpeedListener l) {speedListener = null;}
//规定注销方法
public void processMouseEvent(MouseEvent event){//发送事件的方法
……
if (speedListener != null) { speedListener.getSpeedValue(currentValue);}
}
public class MainPanel extends Panel implements SpeedListener{
………
public void getSpeedValue(int value){…}
//对接口方法的实现,对传入值的具体处理
}
3)使用自定义接口定制多目广播的Listener
很多情形,我们要把一个类实例若干状态的改变,及时地通知其它的多个类实例。这时,需要建立一个多目广播的listener。建立的方法和建立单目广播listener大同小异。一般采用向量表的方法在事件源中规定多个注册、注销方法,并在引发事件的方法中向多个注册的监听器发送事件。
下例中的InputField 类可以同时向已注册的多个类传递参量。InputField类实例把其中输入的字符、当前的插入位置、选定文本的内容及起始终止位置,实时地发送到已向其注册的ShowSelect和Example类实例。
首先,定义了一个接口InputReader,它在参量的发送者和参量的接收着建立了一个双方共同遵守的协议。它和系统的Listener接口起着相同的作用,只不过系统的Listener中规定传递的参量是java.awt.envent包中定义的事件类,而InputReader接口中规定传递的是一组未被封装的数据。
public interface InputReader { //
public void showSelection(int x,int y,String s);
public void showInput(int position,String s);
}
在多目事件源InputField类中,规定了注册和注销方法enableReciever、disableReciever方法,其本质是允许监听器向事件源的注册,以便事件源知道向那些对象发送数据。
public void enableReciever(InputReader cr){//规定注册方法
inputReader=cr;
inputReaderPool.addElement(inputReader);
}
public void disableReciever(InputReader cr){ //规定注销方法
inputReader=cr;
inputReaderPool.removeElement(inputReader);
}
当事件发生时,InputField类使用向量的方法向多个注册的监听器发送事件。 InputReaderPool是一个向量对象,它用来保存向事件源注册的类实例。如果某一时刻只向一个类传递参量,则不必使用向量。
for (int i=0;i<inputReaderPool.size();i++){
inputReader=(InputReader) inputReaderPool.elementAt(i); inputReader.showInput(pos,str);
}
监听器向事件源注册后,便可以监听来自InputField类实例的参量。 本例中,有两个监听器关心来自InputField类实例的参量。
public class Example extends Frame implements InputReader{
public Example(){
……
inputField.enableReciever(this); //Example类实例向InputField注册
inputField.enableReciever(showSelect); // ShowSelect类实例向InputField注册
}
public void showSelection(int x,int y,String s){ }//空实现接口方法, 不做处理
public void showInput(int pos,String s){ showInput.setText(s);} //实现接口方法,处理
}
参考文献
[1] Laurence .JavaBeans从入门到精通.北京: 电子工业出版社,1998
[2]Joseph Weber 等.Java 1.1使用大全(第三版).北京:电子工业出版社.1998
[3] Philip Heller 等.Java高级开发指南.北京:电子工业出版社,1997
|