java事件详解.docx
- 文档编号:26556361
- 上传时间:2023-06-20
- 格式:DOCX
- 页数:11
- 大小:21.38KB
java事件详解.docx
《java事件详解.docx》由会员分享,可在线阅读,更多相关《java事件详解.docx(11页珍藏版)》请在冰豆网上搜索。
java事件详解
java事件详解
目前在一些java应用程序的GUI测试工具,可以提供捕获用户操作的能力并在代码被修改之后能够自动回放用户的操作。
文章将分析Java的事件处理模型及其原理,介绍了基于事件源识别的捕获/回放所需要了解的关键技术并给出了两种实现方式。
1、Java事件介绍
1.1什么是事件
首先我们来回答"什么是事件"这一基本问题。
其实事件本身就是一个抽象的概念,他是表现另一对象状态变化的对象。
在面向对象的程序设计中,事件消息是对象间通信的基本方式。
在图形用户界面程序中,GUI组件对象根据用户的交互产生各种类型的事件消息,这些事件消息由应用程序的事件处理代码捕获,在进行相应的处理后驱动消息响应对象做出反应。
我们在GUI上进行叫化操作的时候,在点击某个可响应的对象时如,按钮,菜单,我们都会期待某个事件的发生。
其实围绕GUI的所有活动都会发生事件,但Java事件处理机制却可以让您挑选出您需要处理的事件。
事件在Java中和其他对象基本是一样的,但有一点不同的是,事件是由系统自动生成自动传递到适当的事件处理程序。
1.2Java事件处理的演变
当java的开发者开始解决用java创建应用程序这一问题时,他们就认识到java事件模型的必要性。
下面对java事件处理的发展做简要的概括。
在JDK1.0的版本采用用的事件模型,提供了基本的事件处理功能。
这是一种包容模型,所有事件都封装在单一的类Event中,所有事件对象都由单一的方法handleEvent来处理,这些定义都在Component类中。
为此,只有Component类的子类才能充当事件处理程序,事件处理传递到组件层次结构,如果目标组件不能完全处理事件,事件被传递到目标组件的容器。
JDK1.1是编程界的一次革命,修正了前面版本的一些缺陷,同时增加了一些重要的新功能如,RMI、JNI、JDBC、JavaBean。
在事件模型上基本框架完全重写,并从Java1.0模型迁移到委托事件模型,在委托模型中事件源生成事件,然后事件处理委托给另一段代码。
从JDK1.2开始,引入了Swing包事件处理模型功能更强大,更加可定制GUI组件与他们相关联的支持类。
在后面的版本基本保持了整个事件模型,但加入了一些附加事件类和接口。
在1.3版本开始引入Rebot类,它能模拟鼠标和键盘事件,并用于自动化测试、自动运行演示、以及其他要求鼠标和键盘控制的应用程序。
我们把JDK1.0事件处理模型成为java1.0事件模型,而从jdk1.1后的版本事件处理模型称为Java2事件处理模型。
2、Java2事件处理模型
在Java1.0事件处理模型中事件处理是以如下方法执行的。
deliverEvent()用于决定事件的目标,目标是处理事件的组件或容器,此过程开始于GUI层的最外部而向内运作。
当按一个button时,如果检测到是该按钮激发的事件,该按钮会访问它的deliverEvent()方法,这一操作由系统完成。
一旦识别目标组件,正确事件类型发往组件的postEvent()方法,该方法依次把事件送到handleEvent()方法并且等待方法的返回值。
"true"表明事件完全处理,"false"将使postEvent()方法联系目标容器,希望完成事件处理。
下面给一个实例:
importjava.applet.*;
importjava.awt.*;
publicclassButton1AppletextendsApplet{
publicvoidinit(){
add(newButton("Red"));
add(newButton("Blue"));
}
publicbooleanaction(Enentevt,ObjectwhatAction){
if(!
(evt.targetinstanceofButton))returnfalse;
Stringbuttonlabel=(String)whatAction;
if(buttonlabel=="Red")setBackground(Color.red);
if(buttonlabel=="Blue")setBackground(Color.blue);
repaint();
returntrue;
}
}
在Java2处理事件时,没有采用dispatchEvent()-postEvent()-handleEvent()方式,采用了监听器类,每个事件类都有相关联的监听器接口。
事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。
对每个明确的事件的发生,都相应地定义一个明确的Java方法。
这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承java.util.EventListener。
实现了事件监听者接口中一些或全部方法的类就是事件监听者。
伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。
事件状态对象作为单参传递给应响应该事件的监听者方法中。
发出某种特定事件的事件源的标识是:
遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。
有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。
我们来看下面一个简单的实例:
importjavax.swing.*;
importjava.awt.*;
importjava.awt.event.*;
publicclassSimpleExampleextendsJFrame{
JButtonjButton1=newJButton();
publicSimpleExample(){
try{
jbInit();
}
catch(Exceptione){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
SimpleExamplesimpleExample=newSimpleExample();
}
privatevoidjbInit()throwsException{
jButton1.setText("jButton1");
jButton1.addActionListener(newSimpleExample_jButton1_actionAdapter(this));
jButton1.addActionListener(newSimpleExample_jButton1_actionAdapter(this));
this.getContentPane().add(jButton1,BorderLayout.CENTER);
this.setVisible(true);
}
voidjButton1_actionPerformed(ActionEvente){
System.exit(0);
}
}
classSimpleExample_jButton1_actionAdapterimplementsjava.awt.event.ActionListener{
SimpleExampleadaptee;
SimpleExample_jButton1_actionAdapter(SimpleExampleadaptee){
this.adaptee=adaptee;
}
publicvoidactionPerformed(ActionEvente){
adaptee.jButton1_actionPerformed(e);
}
}
3、事件捕获与回放
3.1Java事件生命周期
Java事件和万事一样有其生命周期,会出生也会消亡。
下图3.1给出了Java事件生命周期的示意图,事件最初由事件源产生,事件源可以是GUI组件JavaBean或由生成事件能力的对象,在GUI组件情况下,事件源或者是组件的同位体(对于AbstractWindowToolkit[awt]GUI组件来说)或组件本身(对于Swing组件来说)。
事件生成后放在系统事件队列内部。
现在事件处于事件分发线程的控制下。
事件在队列中等待处理,然后事件从事件队列中选出,送到dispatchEvent()方法,dispatchEvent()方法调用processEvent()方法并将事件的一个引用传递给processEvent()方法。
此刻,系统会查看是否有送出事件的位置,如果没有这种事件类型相应的已经注册的监听器,或者如果没有任何组件受到激活来接收事件类型,事件就被抛弃。
当然上图显示的是AWTEvent类的子类的生命周期。
dispatchEvent()方法和processEvent()方法把AWTEvent作为一个参数。
但对,javax.swing.event并不是AWTEvent子类,而是从EventObject直接继承过来,生成这些事件的对象也会定义fireEvent()方法,此方法将事件送到包含在对象监听器列表内的那种类型的任何监听器。
3.2Java事件捕获
从上面的分析我们知道,任何事件产生到dispatchEvent()方法分发方法前,所有的事件都是存放在系统事件的队列中,而且所有的事件都由dispatchEvent()方法来分派。
所以只要能重载dispatchEvent()方法就可以获取系统的所有事件,包括用户输入事件。
一般来说,系统事件队列的操作对用户来说是可以控制。
它在后台自动完成所要完成的事情,使用EventQueue类可以查看甚至操纵系统事件队列。
Java提供了EventQueue类来访问甚至操纵系统事件队列。
EventQueue类中封装了对系统事件队列的各种操作,除dispatchEvent()方法外,其中最关键的是提供了push()方法,允许用特定的EventQueue来代替当前的EventQueue。
只要从EventQueue类中派生一个新类,然后通过push()方法用派生类来代替当前的EventQueue类即可。
这样,所有的系统事件都会转发到派生EventQueue类。
然后,再在派生类中重载dispatchEvent()方法就可以截获所有的系统事件,包括用户输入事件。
下面一段代码给出一个操纵EventQueue的实例:
importjava.awt.*;
importjava.awt.event.*;
publicclassGenerateEventQueueextendsFrameimplementsActionListener{
Buttonbutton1=newButton();
TextFieldtextField1=newTextField();
publicGenerateEventQueue(){
try{
jbInit();
}
catch(Exceptione){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
GenerateEventQueuegenerateEventQueue=newGenerateEventQueue();
}
privatevoidjbInit()throwsException{
button1.setLabel("button1");
button1.addActionListener(this);
textField1.setText("textField1");
this.add(button1,BorderLayout.SOUTH);
this.add(textField1,BorderLayout.CENTER);
EventQueueeq=getToolkit().getSystemEventQueue();
eq.postEvent(newActionEvent(button1,ActionEvent.ACTION_PERFORMED,"test"));
addWindowListener(newWinListener());
setBounds(100,100,300,200);
setVisible(true);
}
publicvoidactionPerformed(ActionEvente){
textField1.setText("eventis:
"+e.getActionCommand());
}
}
classWinListenerextendsWindowAdapter{
publicvoidwindowClosing(WindowEventwe){
System.exit(0);
}
}
运行结果如下图所示:
在文本域中首先出现的是"eventis:
test",这是因为首先得到处理的是EventQueue对象发送到系统事件队列上的ActionEvent。
下面的代码简单说明了如何捕获事件:
importjava.awt.EventQueue;
importjava.awt.*;
importjava.util.*;
publicclassMyQueueEventextendsEventQueue{//定义EventQueue的子类
publicMyQueueEvent(){
}
publicstaticvoidmain(String[]args){
SimpleExample.main(newString[]{null});
MyQueueEventmyQueueEvent1=newMyQueueEvent();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(myQueueEvent1);
}
//在这里重载事件分发的方法
publicvoiddispatchEvent(AWTEventae){
if(ae.getSource()instanceofjavax.swing.JButton)
System.out.println("Myapture:
"+((javax.swing.JButton)ae.getSource()).getText());
super.dispatchEvent(ae);
}
这个程序可以打印出当前应用的所有的事件,可以将这些事件中选出你需要的事件保存当然你还需要解析该控件的特征。
在上面加黑部分的代码,打印事件源控件的名称。
除此之外,还可以通过实现java.awt.event.AWTEventListener接口实现对事件的捕获。
这个侦听器接口可以接收ComponentorMenuComponent以及它们的派生类在整个系统范围内所分发的事件,AWTEventListeners只是被动的监控这些事件。
如果要监控系统事件,除了要实现接口,还要用Toolkit的addAWTEventListener方法注册这个侦听器。
下面我们来看一个实例:
importjava.awt.AWTEvent;
importjava.awt.Frame;
importjava.awt.Toolkit;
importjava.awt.Window;
importjava.awt.event.AWTEventListener;
importjava.awt.event.WindowEvent;
importjava.util.ArrayList;
importjava.lang.ref.WeakReference;
publicclassMyAWTEventListenerimplementsAWTEventListener{
privatestaticMyAWTEventListeners_singleton=null;//保证该类只被初始化一次
publicstaticMyAWTEventListenergetInstance(){
if(s_singleton==null){
s_singleton=newMyAWTEventListener();
}
returns_singleton;
}
privateMyAWTEventListener(){
//注意下面这行代码,如果没有这行代码,将无法接收到系统分发的事件
//下面代码在注册时,只请求了接收WINDOW_EVENT_MASK事件
//但实际上,你可以接收其他AWTEvent中定义的事件类型
Toolkit.getDefaultToolkit().addAWTEventListener(this,AWTEvent.COMPONENT_EVENT_MASK
);
}
/*
这就是接口方法的实现
*/
publicvoideventDispatched(finalAWTEventtheEvent){
processEvent(theEvent);
}
privatestaticvoidprocessEvent(finalAWTEventtheEvent){
System.out.println(theEvent.getSource());//打印事件源
switch(theEvent.getID()){
caseWindowEvent.WINDOW_OPENED:
//System.out.println(((Frame)theEvent.getSource()).getTitle());
caseWindowEvent.WINDOW_ACTIVATED:
caseWindowEvent.WINDOW_DEACTIVATED:
caseWindowEvent.WINDOW_CLOSING:
default:
break;
}
}
}
3.3Java事件回放
事件的回放其实比较简单了,比如我们现在记录的是frame1下的jButton1点击事件回放。
看下面一段简单的程序,只要点一下jButton1,就在控制台打印一次"clickme"的字符串。
importjava.awt.*;
importjavax.swing.*;
importjava.awt.event.*;
publicclassFrame1extendsJFrame{
privateJButtonjButton1=newJButton();
publicFrame1(){
try{
jbInit();
}
catch(Exceptione){
e.printStackTrace();
}
}
publicstaticvoidmain(String[]args){
Frame1frame1=newFrame1();
frame1.setVisible(true);
}
privatevoidjbInit()throwsException{
jButton1.setText("jButton1");
jButton1.addActionListener(newjava.awt.event.ActionListener(){
publicvoidactionPerformed(ActionEvente){
jButton1_actionPerformed(e);
}
});
this.setTitle("Test");
this.getContentPane().add(jButton1,BorderLayout.CENTER);
}
voidjButton1_actionPerformed(ActionEvente){
System.out.println("clickme");
}
}
下面是回放的程序,在下面的程序中用到了java.awt.Robot类,这个类通常用来在自动化测试或程序演示中模拟系统事件,在某些需要控制鼠标或键盘的应用程序中这个类也是很有用,这个类主要的目的就是为方便的实现java的GUI自动化测试平台。
在事件回放时,我们同样需要该类来模拟生成系统的事件,完成记录的操作的回放,在下面的代码中,给出了一个简单的例子。
importjava.awt.*;
importjavax.swing.*;
importjava.awt.event.*;
publicclassTestReplayextendsThread{
publicstaticvoidmain(String[]args){
try{
//启动要回放的应用程序
Frame1.main(newString[]{null});
//等应用程序启动后延迟3秒再进行回放
Thread.currentThread().sleep(3000);
Robotrobottest=newRobot();
robottest.waitForIdle();
//根据标题名获取当前应用的主窗体,在本例中为"test"
Framejframe=getFrame("test");;
//根据给定的窗体和窗体中要find的控件的名称来获取控件的引用
JButtonjbtn=getButton(jframe,"jButton1");
//将鼠标移到控件所在的位置
robottest.mouseMove(jbtn.getLocationOnScreen().x+jbtn.getWidth()/2
jbtn.getLocationOnScreen().y+jbtn.getHeight()/2);
//在控件所在位置,生成鼠标点击事件
robottest.mousePress(InputEvent.BUTTON1_MASK);
robottest.mouseRelease(InputEvent.BUTTON1_MASK);
}catch(Exceptionee){
ee.printStackTrace();
}
}
//获得标题为title的frame
privatestaticFramegetFrame(Stringtitle){
Frame[]jframes=(Frame[])JFrame.getFrames();
for(inti=0;i
if(jframes[i].getTitle().equalsIgnoreCase(title))returnjframes[i];
}
returnnull;
}
//获取某一个frame下的某个名为jButton1的控件
privatestaticJButtongetButton(Framejf,Stringtext){
/*注意下面这行代码,因为实例比较简单只有ContentPane一个Container类型的控件,
如果在JFrame中有多个Container控件//的话,必须进行递归处理,搜索出所有的控件
*/
Component[]coms=((JFrame)jf)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- java 事件 详解