- 浏览: 411491 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
dageda_jgw:
...
JSF定义及原理 -
尘土飞扬:
这个在NB7中已经无效了,加不加都会提示 UTF8打不开
Netbeans统一UTF-8编码的方法 -
ouyangkang20:
不对
Struts target is null for setProperty(null, "x", [Ljava.lang.String;@179da9f) -
wpf523:
hahah,很不错啊
mysql安装图解教程 5.1 -
taburissmeng:
action中相应的属性没有set get方法就会报这样的错。 ...
Struts target is null for setProperty(null, "x", [Ljava.lang.String;@179da9f)
利用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事件处理模型成为java 1.0事件模型,而从jdk1.1后的版本事件处理模型称为Java 2事件处理模型。
2、 Java 2事件处理模型
在Java1.0 事件处理模型中事件处理是以如下方法执行的。deliverEvent()用于决定事件的目标,目标是处理事件的组件或容器,此过程开始于GUI层的最外部而向内运作。当按一个button时,如果检测到是该按钮激发的事件,该按钮会访问它的deliverEvent()方法,这一操作由系统完成。一旦识别目标组件,正确事件类型发往组件的postEvent()方法,该方法依次把事件送到handleEvent()方法并且等待方法的返回值。 "true"表明事件完全处理,"false"将使postEvent()方法联系目标容器,希望完成事件处理。
下面给一个实例:
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;
}
}
在Java2处理事件时,没有采用dispatchEvent()-postEvent()-handleEvent()方式,采用了监听器类,每个事件类都有相关联的监听器接口。事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。
对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承 java.util.EventListener。 实现了事件监听者接口中一些或全部方法的类就是事件监听者。伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。
我们来看下面一个简单的实例:
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);
}
}
3、 事件捕获与回放
3.1 Java事件生命周期
Java事件和万事一样有其生命周期,会出生也会消亡。下图3.1给出了Java事件生命周期的示意图,
事件最初由事件源产生,事件源可以是GUI组件Java Bean或由生成事件能力的对象,在GUI组件情况下,事件源或者是组件的同位体(对于Abstract Window Toolkit[awt]GUI组件来说)或组件本身(对于Swing组件来说)。事件生成后放在系统事件队列内部。现在事件处于事件分发线程的控制下。事件在队列中等待处理,然后事件从事件队列中选出,送到dispatchEvent()方法,dispatchEvent()方法调用 processEvent()方法并将事件的一个引用传递给processEvent()方法。此刻,系统会查看是否有送出事件的位置,如果没有这种事件类型相应的已经注册的监听器,或者如果没有任何组件受到激活来接收事件类型,事件就被抛弃。当然上图显示的是AWTEvent类的子类的生命周期。 dispatchEvent()方法和processEvent()方法把AWTEvent作为一个参数。但对,javax.swing.event并不是AWTEvent子类,而是从EventObject直接继承过来,生成这些事件的对象也会定义fireEvent()方法,此方法将事件送到包含在对象监听器列表内的那种类型的任何监听器。
3.2 Java事件捕获
从上面的分析我们知道,任何事件产生到dispatchEvent()方法分发方法前,所有的事件都是存放在系统事件的队列中,而且所有的事件都由 dispatchEvent()方法来分派。所以只要能重载dispatchEvent()方法就可以获取系统的所有事件,包括用户输入事件。一般来说,系统事件队列的操作对用户来说是可以控制。它在后台自动完成所要完成的事情,使用EventQueue类可以查看甚至操纵系统事件队列。
Java 提供了EventQueue类来访问甚至操纵系统事件队列。EventQueue类中封装了对系统事件队列的各种操作,除dispatchEvent() 方法外,其中最关键的是提供了push()方法,允许用特定的EventQueue来代替当前的EventQueue。只要从EventQueue类中派生一个新类,然后通过push()方法用派生类来代替当前的EventQueue类即可。这样,所有的系统事件都会转发到派生EventQueue类。然后,再在派生类中重载dispatchEvent()方法就可以截获所有的系统事件,包括用户输入事件。下面一段代码给出一个操纵EventQueue的实例:
import java.awt.*;
import java.awt.event.*;
public class GenerateEventQueue extends Frame implements ActionListener{
Button button1 = new Button();
TextField textField1 = new TextField();
public GenerateEventQueue() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
GenerateEventQueue generateEventQueue = new GenerateEventQueue();
}
private void jbInit() throws Exception {
button1.setLabel("button1");
button1.addActionListener(this) ;
textField1.setText("textField1");
this.add(button1, BorderLayout.SOUTH);
this.add(textField1, BorderLayout.CENTER);
EventQueue eq=getToolkit().getSystemEventQueue() ;
eq.postEvent(new ActionEvent(button1,ActionEvent.ACTION_PERFORMED,"test" )) ;
addWindowListener(new WinListener());
setBounds(100,100,300,200);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
textField1.setText("event is :" e.getActionCommand()) ;
}
}
class WinListener extends WindowAdapter{
public void windowClosing(WindowEvent we){
System.exit(0) ;
}
}
运行结果如下图所示:
在文本域中首先出现的是"event is :test",这是因为首先得到处理的是EventQueue对象发送到系统事件队列上的ActionEvent。
下面的代码简单说明了如何捕获事件:
import java.awt.EventQueue;
import java.awt.*;
import java.util.*;
public class MyQueueEvent extends EventQueue {//定义EventQueue的子类
public MyQueueEvent() {
}
public static void main(String[] args) {
SimpleExample.main(new String[]{null}) ;
MyQueueEvent myQueueEvent1 = new MyQueueEvent();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(myQueueEvent1) ;
}
//在这里重载事件分发的方法
public void dispatchEvent(AWTEvent ae){
if(ae.getSource() instanceof javax.swing.JButton)
System.out.println("My apture:" ((javax.swing.JButton)ae.getSource()).getText()) ;
super.dispatchEvent(ae);
}
这个程序可以打印出当前应用的所有的事件,可以将这些事件中选出你需要的事件保存当然你还需要解析该控件的特征。在上面加黑部分的代码,打印事件源控件的名称。
除此之外,还可以通过实现java.awt.event. AWTEventListener接口实现对事件的捕获。这个侦听器接口可以接收Component or MenuComponent 以及它们的派生类在整个系统范围内所分发的事件,AWTEventListeners只是被动的监控这些事件。如果要监控系统事件,除了要实现接口,还要用Toolkit的addAWTEventListener方法注册这个侦听器。
下面我们来看一个实例:
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;
}
}
}
3.3 Java事件回放
事件的回放其实比较简单了,比如我们现在记录的是frame1下的jButton1点击事件回放。看下面一段简单的程序,只要点一下jButton1,就在控制台打印一次"click me"的字符串。
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Frame1 extends JFrame {
private JButton jButton1 = new JButton();
public Frame1() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Frame1 frame1 = new Frame1();
frame1.setVisible(true) ;
}
private void jbInit() throws Exception {
jButton1.setText("jButton1");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
this.setTitle("Test");
this.getContentPane().add(jButton1, BorderLayout.CENTER);
}
void jButton1_actionPerformed(ActionEvent e) {
System.out.println("click me") ;
}
}
下面是回放的程序,在下面的程序中用到了 java.awt.Robot类,这个类通常用来在自动化测试或程序演示中模拟系统事件,在某些需要控制鼠标或键盘的应用程序中这个类也是很有用,这个类主要的目的就是为方便的实现java的GUI自动化测试平台。在事件回放时,我们同样需要该类来模拟生成系统的事件,完成记录的操作的回放,在下面的代码中,给出了一个简单的例子。
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<jframes.length ;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<coms.length ;i ){
if(!(coms[i] instanceof JButton))continue;
if(((JButton)coms[i]).getText().equalsIgnoreCase(text))return (JButton)coms[i];
}
return null;
}
public void run(){
}
}
该程序运行完,你会发现在控制台同样打印出了:
"click me"的字符串说明事件被正确回放了。
当然还可以通过直接操纵系统事件队列实现输入事件的回放。先通过记录下的窗口/组件名获得对应窗口引用,然后重构鼠标/键盘事件,最后将重构的事件直接放入系统事件队列,由分派线程执行后续的事件分派工作。还需要解决关键问题如何能根据窗口名称获得其引用。这里还是可以通过系统事件队列来实现的,因为 Java程序在新建/删除一个容器时都会向系统事件队列发出一个Containerevent事件,其中包含了对该容器的引用。所以,事件回放器在载入被测测试程序后便监视系统队列,截获所有的Containerevent事件。如果新建容器,便获得新建Container的引用。因为所有的 Container都实现了getComponets(),可以返回所有该容器所包含的组件或容器,只需要保存到一个HashMap结构中,需要时检索出来就可以了。该过程所用到的知识,其实在上面都有提到而且在实际引用中,既然Robot已经帮我们完成许多事情,也没有必要自己再去重构一个鼠标或键盘事件了,不过有兴趣的朋友也可以去试试。
4、 结束语
随着我国软件业的发展,软件测试技术作为软件质量保证的重要环节越来越受到重视,而在基于GUI的应用中采用自动化测试工具可以提高软件测试的有效性和效率,特别在回归测试中可以大大减少人力投入,还可以提高测试脚本的复用。因此,软件自动测试平台开发已经成为软件测试的一个重要领域。本文介绍了基于 Java的GUI应用的自动测试平台开发需要的基本但关键的捕获、回放功能,所有相关系统开发其实都离不开本文说的方法。
来源于: http://www.cnham.com/unix/html/24/2006/0312/6265.html
Java事件处理
从概念上讲,事件是一种在"源对象"和"监听者对象"之间,某种状态发生变化的传递机制。事件有许多不同的用途,例如在Windows系统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在Java中则是定义了一个普通的、可扩充的事件机制,这种机制能够:
对事件类型和传递的模型的定义和扩充提供一个公共框架,并适合于广泛的应用。
与Java语言和环境有较高的集成度。
事件能被描述环境捕获和触发。
能使其它构造工具采取某种技术在设计时直接控制事件,以及事件源和事件监听者之间的联系。
事件机制本身不依赖于复杂的开发工具。
事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。 对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承java.util.EventListener。实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。
事件状态对象(Event State Object)
与事件发生有关的状态信息一般都封装在一个事件状态对象中,这种对象是java。util。EventObject的子类。按设计习惯,这种事件状态对象类的名应以Event结尾。例如:
public class MouseMovedExampleEvent extends java。util。EventObject
{ protected int x, y;
/* 创建一个鼠标移动事件MouseMovedExampleEvent */
MouseMovedExampleEvent(java.awt.Component source, Point location) {
super(source);
x = location.x;
y = location.y;
}
/* 获取鼠标位置*/
public Point getLocation() {
return new Point(x, y);
}}
目前在一些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事件处理模型成为java 1.0事件模型,而从jdk1.1后的版本事件处理模型称为Java 2事件处理模型。
2、 Java 2事件处理模型
在Java1.0 事件处理模型中事件处理是以如下方法执行的。deliverEvent()用于决定事件的目标,目标是处理事件的组件或容器,此过程开始于GUI层的最外部而向内运作。当按一个button时,如果检测到是该按钮激发的事件,该按钮会访问它的deliverEvent()方法,这一操作由系统完成。一旦识别目标组件,正确事件类型发往组件的postEvent()方法,该方法依次把事件送到handleEvent()方法并且等待方法的返回值。 "true"表明事件完全处理,"false"将使postEvent()方法联系目标容器,希望完成事件处理。
下面给一个实例:
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;
}
}
在Java2处理事件时,没有采用dispatchEvent()-postEvent()-handleEvent()方式,采用了监听器类,每个事件类都有相关联的监听器接口。事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。
对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承 java.util.EventListener。 实现了事件监听者接口中一些或全部方法的类就是事件监听者。伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。
我们来看下面一个简单的实例:
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);
}
}
3、 事件捕获与回放
3.1 Java事件生命周期
Java事件和万事一样有其生命周期,会出生也会消亡。下图3.1给出了Java事件生命周期的示意图,
事件最初由事件源产生,事件源可以是GUI组件Java Bean或由生成事件能力的对象,在GUI组件情况下,事件源或者是组件的同位体(对于Abstract Window Toolkit[awt]GUI组件来说)或组件本身(对于Swing组件来说)。事件生成后放在系统事件队列内部。现在事件处于事件分发线程的控制下。事件在队列中等待处理,然后事件从事件队列中选出,送到dispatchEvent()方法,dispatchEvent()方法调用 processEvent()方法并将事件的一个引用传递给processEvent()方法。此刻,系统会查看是否有送出事件的位置,如果没有这种事件类型相应的已经注册的监听器,或者如果没有任何组件受到激活来接收事件类型,事件就被抛弃。当然上图显示的是AWTEvent类的子类的生命周期。 dispatchEvent()方法和processEvent()方法把AWTEvent作为一个参数。但对,javax.swing.event并不是AWTEvent子类,而是从EventObject直接继承过来,生成这些事件的对象也会定义fireEvent()方法,此方法将事件送到包含在对象监听器列表内的那种类型的任何监听器。
3.2 Java事件捕获
从上面的分析我们知道,任何事件产生到dispatchEvent()方法分发方法前,所有的事件都是存放在系统事件的队列中,而且所有的事件都由 dispatchEvent()方法来分派。所以只要能重载dispatchEvent()方法就可以获取系统的所有事件,包括用户输入事件。一般来说,系统事件队列的操作对用户来说是可以控制。它在后台自动完成所要完成的事情,使用EventQueue类可以查看甚至操纵系统事件队列。
Java 提供了EventQueue类来访问甚至操纵系统事件队列。EventQueue类中封装了对系统事件队列的各种操作,除dispatchEvent() 方法外,其中最关键的是提供了push()方法,允许用特定的EventQueue来代替当前的EventQueue。只要从EventQueue类中派生一个新类,然后通过push()方法用派生类来代替当前的EventQueue类即可。这样,所有的系统事件都会转发到派生EventQueue类。然后,再在派生类中重载dispatchEvent()方法就可以截获所有的系统事件,包括用户输入事件。下面一段代码给出一个操纵EventQueue的实例:
import java.awt.*;
import java.awt.event.*;
public class GenerateEventQueue extends Frame implements ActionListener{
Button button1 = new Button();
TextField textField1 = new TextField();
public GenerateEventQueue() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
GenerateEventQueue generateEventQueue = new GenerateEventQueue();
}
private void jbInit() throws Exception {
button1.setLabel("button1");
button1.addActionListener(this) ;
textField1.setText("textField1");
this.add(button1, BorderLayout.SOUTH);
this.add(textField1, BorderLayout.CENTER);
EventQueue eq=getToolkit().getSystemEventQueue() ;
eq.postEvent(new ActionEvent(button1,ActionEvent.ACTION_PERFORMED,"test" )) ;
addWindowListener(new WinListener());
setBounds(100,100,300,200);
setVisible(true);
}
public void actionPerformed(ActionEvent e) {
textField1.setText("event is :" e.getActionCommand()) ;
}
}
class WinListener extends WindowAdapter{
public void windowClosing(WindowEvent we){
System.exit(0) ;
}
}
运行结果如下图所示:
在文本域中首先出现的是"event is :test",这是因为首先得到处理的是EventQueue对象发送到系统事件队列上的ActionEvent。
下面的代码简单说明了如何捕获事件:
import java.awt.EventQueue;
import java.awt.*;
import java.util.*;
public class MyQueueEvent extends EventQueue {//定义EventQueue的子类
public MyQueueEvent() {
}
public static void main(String[] args) {
SimpleExample.main(new String[]{null}) ;
MyQueueEvent myQueueEvent1 = new MyQueueEvent();
Toolkit.getDefaultToolkit().getSystemEventQueue().push(myQueueEvent1) ;
}
//在这里重载事件分发的方法
public void dispatchEvent(AWTEvent ae){
if(ae.getSource() instanceof javax.swing.JButton)
System.out.println("My apture:" ((javax.swing.JButton)ae.getSource()).getText()) ;
super.dispatchEvent(ae);
}
这个程序可以打印出当前应用的所有的事件,可以将这些事件中选出你需要的事件保存当然你还需要解析该控件的特征。在上面加黑部分的代码,打印事件源控件的名称。
除此之外,还可以通过实现java.awt.event. AWTEventListener接口实现对事件的捕获。这个侦听器接口可以接收Component or MenuComponent 以及它们的派生类在整个系统范围内所分发的事件,AWTEventListeners只是被动的监控这些事件。如果要监控系统事件,除了要实现接口,还要用Toolkit的addAWTEventListener方法注册这个侦听器。
下面我们来看一个实例:
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;
}
}
}
3.3 Java事件回放
事件的回放其实比较简单了,比如我们现在记录的是frame1下的jButton1点击事件回放。看下面一段简单的程序,只要点一下jButton1,就在控制台打印一次"click me"的字符串。
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Frame1 extends JFrame {
private JButton jButton1 = new JButton();
public Frame1() {
try {
jbInit();
}
catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Frame1 frame1 = new Frame1();
frame1.setVisible(true) ;
}
private void jbInit() throws Exception {
jButton1.setText("jButton1");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
this.setTitle("Test");
this.getContentPane().add(jButton1, BorderLayout.CENTER);
}
void jButton1_actionPerformed(ActionEvent e) {
System.out.println("click me") ;
}
}
下面是回放的程序,在下面的程序中用到了 java.awt.Robot类,这个类通常用来在自动化测试或程序演示中模拟系统事件,在某些需要控制鼠标或键盘的应用程序中这个类也是很有用,这个类主要的目的就是为方便的实现java的GUI自动化测试平台。在事件回放时,我们同样需要该类来模拟生成系统的事件,完成记录的操作的回放,在下面的代码中,给出了一个简单的例子。
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<jframes.length ;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<coms.length ;i ){
if(!(coms[i] instanceof JButton))continue;
if(((JButton)coms[i]).getText().equalsIgnoreCase(text))return (JButton)coms[i];
}
return null;
}
public void run(){
}
}
该程序运行完,你会发现在控制台同样打印出了:
"click me"的字符串说明事件被正确回放了。
当然还可以通过直接操纵系统事件队列实现输入事件的回放。先通过记录下的窗口/组件名获得对应窗口引用,然后重构鼠标/键盘事件,最后将重构的事件直接放入系统事件队列,由分派线程执行后续的事件分派工作。还需要解决关键问题如何能根据窗口名称获得其引用。这里还是可以通过系统事件队列来实现的,因为 Java程序在新建/删除一个容器时都会向系统事件队列发出一个Containerevent事件,其中包含了对该容器的引用。所以,事件回放器在载入被测测试程序后便监视系统队列,截获所有的Containerevent事件。如果新建容器,便获得新建Container的引用。因为所有的 Container都实现了getComponets(),可以返回所有该容器所包含的组件或容器,只需要保存到一个HashMap结构中,需要时检索出来就可以了。该过程所用到的知识,其实在上面都有提到而且在实际引用中,既然Robot已经帮我们完成许多事情,也没有必要自己再去重构一个鼠标或键盘事件了,不过有兴趣的朋友也可以去试试。
4、 结束语
随着我国软件业的发展,软件测试技术作为软件质量保证的重要环节越来越受到重视,而在基于GUI的应用中采用自动化测试工具可以提高软件测试的有效性和效率,特别在回归测试中可以大大减少人力投入,还可以提高测试脚本的复用。因此,软件自动测试平台开发已经成为软件测试的一个重要领域。本文介绍了基于 Java的GUI应用的自动测试平台开发需要的基本但关键的捕获、回放功能,所有相关系统开发其实都离不开本文说的方法。
来源于: http://www.cnham.com/unix/html/24/2006/0312/6265.html
Java事件处理
从概念上讲,事件是一种在"源对象"和"监听者对象"之间,某种状态发生变化的传递机制。事件有许多不同的用途,例如在Windows系统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在Java中则是定义了一个普通的、可扩充的事件机制,这种机制能够:
对事件类型和传递的模型的定义和扩充提供一个公共框架,并适合于广泛的应用。
与Java语言和环境有较高的集成度。
事件能被描述环境捕获和触发。
能使其它构造工具采取某种技术在设计时直接控制事件,以及事件源和事件监听者之间的联系。
事件机制本身不依赖于复杂的开发工具。
事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。 对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承java.util.EventListener。实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。
事件状态对象(Event State Object)
与事件发生有关的状态信息一般都封装在一个事件状态对象中,这种对象是java。util。EventObject的子类。按设计习惯,这种事件状态对象类的名应以Event结尾。例如:
public class MouseMovedExampleEvent extends java。util。EventObject
{ protected int x, y;
/* 创建一个鼠标移动事件MouseMovedExampleEvent */
MouseMovedExampleEvent(java.awt.Component source, Point location) {
super(source);
x = location.x;
y = location.y;
}
/* 获取鼠标位置*/
public Point getLocation() {
return new Point(x, y);
}}
发表评论
-
如何获得JVM的最大可用内存
2010-06-26 17:47 2123在命令行下用 java -XmxXXXXM -version ... -
java程序的时间与系统时间不同
2010-04-15 21:43 1550实施软件时,windows时区、语言设置都正常,但java程序 ... -
java集合框架全面进阶
2009-05-22 20:07 1161java集合框架全面进阶 java集合框架全面进 ... -
Jsp+JavaBean教程
2009-05-22 18:50 3846按着Sun公司的定义,JavaBean是一个可重复使用的软件组 ... -
Java常用术语解释
2009-05-22 17:39 2340名词 解释 AAA ... -
Java面向对象概述
2009-05-22 17:23 1063(如今的程序领域,大家都在说,面向对象OOP,但是真正能理解 ... -
java入门与加深
2009-05-22 17:13 9721 什么是Java、Java2、JDK ... -
数据类型
2009-05-22 17:00 1138【导读】Java 提供 ... -
java数据类型转换
2009-05-22 16:57 1190[收藏]java数据类型转换 一些初学JAVA ... -
Java异常学习心得
2009-05-22 16:47 1463Java 异常学习心得 本文重在 Java ... -
多线程
2009-05-22 16:26 2178JAVA 教程:解析Java 的多线程机制 ... -
哈希表
2009-05-22 16:23 1521哈希表的概念作用及意义,哈希表的构造方法 ... -
Java接口常用方法
2009-05-22 16:20 2872接口是实现构件可插入性的关键,可插入构件的关键在于存在一 ... -
一个简单的SWT程序实例及详解
2009-05-22 16:04 1778网易学院 一个 ... -
String的比较
2009-05-22 15:34 1338关于java中的equals() 悬 ... -
JVM(JAVA虚拟机介绍)
2009-05-22 15:30 1230JVM(JAVA虚拟机介绍) 来 ... -
如果安装了JDK,会发同你的电脑有两套JRE
2009-05-22 15:29 1449如果安装了JDK,会发同 ... -
文件选择对话框,颜色选择对话框,分隔线的使用与介绍
2009-05-22 15:12 2687文件选择对话框,颜色选择对话框,分隔线的使用与介绍 ++ ... -
Java, JVM, JRE, Java API, Java类库, 编译器, 解释器,
2009-05-22 14:57 2532Java, JVM, JRE, Ja ... -
根据CLASSPATH环境变量定位类
2009-05-17 17:47 2353实际上,如果使用1.5以上版本的JDK,完全可以不用设置CLA ...
相关推荐
在本文中,我们将深入探讨如何利用Java事件处理机制实现录制和回放功能,这种功能常见于GUI测试工具中,用于捕获用户操作并在代码修改后自动回放这些操作。 1. Java 事件介绍 - **事件是什么**:在Java中,事件是...
2. 视频流处理:SDK提供了获取实时视频流、录制视频、回放录像的功能。开发者可以通过这些接口实现监控画面的实时显示,或者进行视频存储和检索。 3. 图像处理:SDK可能包含图像抓取、编码解码、图像分析等模块,...
JavaSound API 是Java平台上用于处理音频和MIDI数据的核心组件,它允许开发者创建音频播放、录制和处理程序。...利用JavaSound API,可以轻松实现各种音频功能,同时通过SPI插件机制扩展到更广泛的音频格式支持。
在这个Java CTI开发案例中,我们将深入学习如何利用Java编程语言实现这些功能。 1. **Java CTI框架理解**:Java CTI通常基于各种API和框架,如JTAPI(Java Telephony Application Programming Interface)或CTLib。...
本资源“java初学者小鱼制作鼠标宏.zip”是针对Java初学者设计的一个项目,旨在帮助学习者理解如何利用Java编程实现鼠标宏功能。鼠标宏是一种自动化工具,可以记录并重复执行一系列鼠标动作,提高工作效率,尤其是在...
2. **界面设计**:使用Java Swing技术构建用户界面,包括棋盘的绘制和棋子的图形化展示,同时实现事件处理机制,响应用户的操作。 3. **功能模块**: - **网络功能**:通过Socket编程实现客户端与服务器的连接,...
在实际开发中,我们可以通过阅读和学习jEdit的源码,了解如何利用Java实现类似的功能,例如如何设计高效的文本处理机制,如何构建模块化的应用程序框架,以及如何实现丰富的用户界面交互。此外,jEdit的源码也是学习...
通过Java,开发者可以创建一个强大的服务器端,处理视频通话的逻辑,如信令交换、媒体流路由和录制等功能。 **实现步骤** 1. **信令交换**:使用WebSocket进行用户间的信令交互,如呼叫邀请、接听确认、挂断通知等...
1. **Java绑定**:Selenium 的Java绑定允许开发者用Java语言编写测试脚本,利用Java的强大特性和丰富的库来实现复杂的测试逻辑。这包括使用JUnit或TestNG等测试框架,以及与Spring等企业级框架的集成。 2. **...
10. 回放功能:虽然Selenium主要用于自动化测试,但也可以作为录制和回放工具,帮助开发者快速理解网页交互逻辑。 在实际使用 "selenium-java-2.45.0" 时,开发者会下载包含相应jar文件的压缩包,将其引入项目依赖...
4. 录制和回放功能:QTP的录制功能能够自动记录用户在应用程序中的交互动作,然后将其转化为测试脚本。回放时,QTP执行这些动作来验证应用程序的行为。录制选项包括启动录制时是否只记录启动应用程序的动作,还是...
海康威视网络摄像机SDK是一款专为开发者设计的软件开发工具包,它允许程序员通过编程接口与海康威视的网络摄像机进行交互,实现远程监控、视频流处理、录像回放等功能。这款SDK主要面向Java开发者,因此在标签中提到...
总之,这个压缩包中的源码实例是学习如何在Android上利用JavaCV进行录像的一个宝贵资源,涵盖了摄像头操作、音视频编码、权限管理等多个方面,对于提升Android应用开发能力非常有帮助。通过深入研究和实践,开发者...
- **直播录制与回放**:支持对直播内容的录制和回放功能。 4. **API详解** SDK中的API涵盖了从创建直播任务、设置推流参数、控制直播状态到获取直播统计信息等全过程。每个API都有详细的文档说明,帮助开发者理解...
- **媒体录制**:允许用户记录聊天过程,供后续回放。 在实际开发中,"chat"可能是项目文件夹,包含了JSP页面、JavaScript脚本、CSS样式表、HTML模板以及其他必要的资源文件。开发者需要将这些元素整合,创建出一...
本文将深入探讨eSDK IVS(Intelligent Video Surveillance)V100R005C10的开发过程,主要关注服务端Native接口以及如何利用Java语言进行第三方应用的集成和业务开发。eSDK IVS是华为提供的智能视频监控平台,它提供...
- **RFT 的主要功能**:详细介绍RFT提供的核心功能,如对象识别、脚本录制与回放等。 - **安装与配置**:指导学员完成RFT的安装过程,并配置所需的开发环境。 ##### 模块2:Java 脚本编程基础 - **Java 环境搭建**...
4. **音频处理**:Java Sound API是Java提供的一套音频处理工具,它支持音频的录制、播放、混音等功能。在游戏里,开发者可能使用这个API来捕获用户的语音,然后进行回放或者处理,使汤姆猫能够“复述”用户的话。 ...