目前在一些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后的版本事件处理模型称为Java 2事件处理模型。
2、Java 2事件处理模型
在Java1.0事件处理模型中事件处理是以如下方法执行的。deliverEvent()用于决定事件的目标,目标是处理事件的组件或容器,此过程开始于GUI层的最外部而向内运作。
import java.applet.*;
import java.awt.*;
public class Button1Applet
extends Applet{
public void init()
{
add(new Button("Red"));
add(new Button("Blue"));
}
public boolean action
(Enent evt,Object whatAction)
{
if( !( evt.target
instanceof Button))return false;
String buttonlabel=
(String)whatAction;
if(buttonlabel=="Red")
setBackground(Color.red);
if(buttonlabel==" Blue")
setBackground(Color.blue);
repaint();
return true;
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class SimpleExample extends JFrame
{
JButton jButton1 = new JButton();
public SimpleExample()
{
try {
jbInit();
}
catch(Exception e)
{
e.PRintStackTrace();
}
}
public static void
main(String[] args)
{
SimpleExample simpleExample
= new SimpleExample();
}
private void jbInit()
throws Exception {
jButton1.setText
("jButton1");
jButton1.addActionListener(new SimpleExample_jButton1_actionAdapter(this));
jButton1.addActionListener(new SimpleExample_jButton1_actionAdapter(this));
this.getContentPane().add
(jButton1, BorderLayout.CENTER);
this.setVisible(true);
}
void jButton1_actionPerformed
(ActionEvent e)
{
System.exit(0);
}
}
class SimpleExample_jButton1_
actionAdapter implements
java.awt.event.ActionListener
{
SimpleExample adaptee;
SimpleExample_jButton1_actionAdapter
(SimpleExample adaptee)
{
this.adaptee = adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.jButton1_actionPerformed(e);
}
}
import java.awt.*;
import java.awt.event.*;
public class GenerateEventQueue
import java.awt.EventQueue;
import java.awt.*;
import java.util.*;
import java.awt.AWTEvent;
import java.awt.Frame;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.AWTEventListener;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.lang.ref.WeakReference;
public class MyAWTEventListener
implements AWTEventListener
{
private static MyAWTEventListener
s_singleton = null;
//保证该类只被初始化一次
public static
MyAWTEventListener getInstance()
{
if(s_singleton==null)
{
s_singleton=new MyAWTEventListener();
}
return s_singleton;
}
private MyAWTEventListener()
{
//注重下面这行代码,假如没有这行代码,
将无法接收到系统分发的事件
// 下面代码在注册时,
只请求了接收WINDOW_EVENT_MASK事件
//但实际上,
你可以接收其他AWTEvent中定义的事件类型
Toolkit.getDefaultToolkit().addAWTEventListener
(this,
AWTEvent.COMPONENT_EVENT_MASK
);
}
/*
这就是接口方法的实现
*/
public void eventDispatched
(final AWTEvent theEvent)
{
processEvent(theEvent);
}
private static void processEvent
(final AWTEvent theEvent)
{
System.out.println
(theEvent.getSource() ) ;
//打印事件源
switch (theEvent.getID())
{
case WindowEvent.WINDOW_OPENED:
//System.out.println
(((Frame)theEvent.getSource()).getTitle() ) ;
case WindowEvent.WINDOW_ACTIVATED:
case WindowEvent.WINDOW_DEACTIVATED:
case WindowEvent.WINDOW_CLOSING:
default: break;
}
}
}
import java.awt.*;
import javax.swing.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class TestReplay extends Thread
{
public static void
main(String[] args)
{
try{
//启动要回放的应用程序
Frame1.main(new String[]{null}) ;
//等应用程序启动后延迟3秒再进行回放
Thread.currentThread().sleep(3000) ;
Robot robottest=new Robot();
robottest.waitForIdle();
//根据标题名获取当前应用的主窗体,
在本例中为"test"
Frame jframe=getFrame("test");;
//根据给定的窗体和窗体中要
find的控件的名称来获取控件的引用
JButton jBTn=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(Exception ee){
ee.printStackTrace() ;
}
}
//获得标题为title的frame
private static Frame
getFrame(String title)
{
Frame[] jframes=(Frame[])
JFrame.getFrames();
for(int i=0;i
{
if(jframes[i].getTitle().
equalsIgnoreCase(title))return jframes[i];
}
return null;
}
//获取某一个frame下的某个名为jButton1的控件
private static JButton
getButton(Frame jf,String text)
{
/*注重下面这行代码,因为实例比较简单
只有ContentPane一个Container类型的控件,
假如在JFrame中有多个Container控件
//的话,必须进行递归处理,搜索出所有的控件
*/
Component[] coms=((JFrame)jf).
getContentPane().getComponents();
for(int i=0;i
{
if(!(coms[i] instanceof
JButton))continue;
if(((JButton)coms[i]).
getText().equalsIgnoreCase(text))
return (JButton)coms[i];
}
return null;
}
public void run(){
}
}
新闻热点
疑难解答