目前在一些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層的最外部而向內運作。
<!-- frame contents -->
<!-- /frame contents --> 當按一個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;
}
}
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
<!-- frame contents -->
<!-- /frame contents --> 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) ;
}
}
import java.awt.EventQueue;
import java.awt.*;
import java.util.*;
<!-- frame contents -->
<!-- /frame contents --> 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);
}
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.*;
<!-- frame contents -->
<!-- /frame contents --> 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") ;
}
}
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(){
}
}