1. 程式人生 > >Java —— 事件處理機制

Java —— 事件處理機制

一、Java事件主要角色

Source:事件源,即觸發事件的物件;

EventObject:事件物件,即帶有 EventSource 資訊的事件物件,是對EventSource的包裝;

Eventlistener:事件監聽器,對該事件的處理。

說明:

1、Source:即任何具有行為的Java 物件,具有行為是為了能觸發事件。

2、EventObject類:

繼承關係:直接繼承於Object ,實現了Serializable介面,是所有事件物件的父類(如介面程式設計中Action事件類的父類AWTEvent)。定義於java.util包。

EventObject提供了3個基本方法:

構造方法:EventObject(Object source)//source不能為null
普通方法:
Object getSource();//返回事件源
String toString();//返回該事件類名與事件源名

3、EventListener 介面:

繼承關係:直接繼承於Object,所有監聽器的父介面。定義於java.util包。無方法。

二、Java 事件實現過程與觸發過程

實現過程:

首先,為要觸發事件的物件(source)定義事件物件;

其次,為事件物件定義事件監聽器;

最後,定義事件源(source)的類,指定新增監聽器的方法。

觸發過程:

以按鈕點選事件(屬於Action事件)為例,首先我們通過繼承於元件的addActionListener 方法,為按鈕添加了ActionListener 介面實現類,那麼按鈕類中定義的與點選相關的某個方法A 就會把自身作為source建立一個事件物件,並將該事件物件傳入addActionListener 方法所新增的監聽器中,在監聽器中根據source 型別或例項選擇執行某段程式碼,如此便完成了事件的觸發。

需要注意的是,元件(如JFram、Button等)觸發Action 等事件時事件物件的建立是由JVM 自動完成的,而不是通過開發人員編碼實現,即我們不關注Action 事件觸發的細節,而是隻關注 ActionListener 實現類中對業務的處理。

由此,可簡單理解三個物件的作用為:

事件源(source):其一、規定事件由誰來產生;其二、在listener 中作路由;

事件物件(EventObject):起到傳遞事件資訊的作用。是對source 的包裝,根據不同的構造方式,可以附帶除source外的其它資訊;

事件監聽器(EventListener):業務關注點。根據事件物件中的事件源(source)或事件物件型別進行路由選擇,實現不同業務。

通過程式碼加深理解:

1、EventObject 建構函式解讀:以ActionEvent 其中一個建構函式為例

 public ActionEvent(Object source, int id, String command, int modifiers) {
        this(source, id, command, 0, modifiers);
    }


最終其實呼叫了另外一個建構函式,可以看出ActionEvent 中多了事件id、事件相關的字串command(對輸入框此值即是輸入的值)、modifiers(代表事件期間是否按下shift, ctrl, alt, meta),此處0其實代表了一個long型別引數,代表修改時間。可見,ActionListener 在EventLIstener 基礎上添加了除source 外的更多資訊,可類比其他事件。

2、awt 或swing 介面程式設計中對事件的典型處理過程:以Button 的點選事件(屬於ActionEvent)為例

首先,定義事件物件。此過程省略,java 自動實現了。

其次,定義監聽器。如下:

class MyActionListener implements ActionListener{
    @Override
    public void actionPerformed(ActionEvent e){
    //定義發生ActionEvent 事件時要執行什麼。當然,一個監聽器是可以監聽多個事件源觸發的ActionListener的,所以通常都會先判斷事件源
        if(e.getSource() == myButtonOne){...}
        else if(e.getSource()==myButtonTwo{...}
    ...
    }
}


說明:ActionEvent 不是我們自己定義的,所以不知道點選按鈕時,按鈕物件產生該ActionEvent 物件的細節,當然可讀原始碼。如果是自己定義一些事件與事件源,則在事件源內部何時產生事件物件及怎樣產生事件物件完全由我們自己定義。上面程式碼是通過==判斷事件源的,這要求該類能直接訪問到事件源物件。監聽器的函式引數也有文章可做,可通過IoC的思想,傳入實現監聽器介面的不同類,通過類的不同來處理不同業務。

if(e instanceof MyEvent)//不關注事件源,而關注事件型別時,通過事件型別判斷
if(e.getSource() instanceof MyEventSource)//型別已確定,關注事件來源時,通過事件源型別判斷

最後,註冊監聽器。

JButton myButtonOne=new JButton("按鈕一");
JButton myButtonTwo=new JButton("按鈕二");
MyActionListener listener=new MyActionListener();
myButtonOne.addActionListener(listener);
myButtonTwo.addActionListener(listerner);


監聽器理解:監聽器本身就是實現了監聽介面,所以其本質作用於介面相同,即將我們的關注的業務從事件源中抽離出來,便於管理。對於多個事件源的監聽器,每次都只是根據事件源或事件型別執行了對應的那段程式碼。如果對addActionListener 後事件具體怎麼產生的還是有疑問,那就看看第三點!

三、編寫自己的事件模擬Java 事件處理

目的:瞭解事件是怎麼自動觸發的(事件源類是關鍵)。

程式碼:

/**
 * 事件類。引數source起路由作用,判斷事件來源,作用相當於JSP
 *  的servlet。此處定義了兩個事件源,實際第二個沒用到,只是想說明可以通過注入介面或超類的思想將事件類別作為判斷依據。
 */
class EventClassOne extends EventObject{
    
    public EventClassOne(Object source) {
        /*選擇執行哪中構造方式,此處只有EventObject(Object source)一種*/
        super(source);
    }
}
class EventClassTwo extends EventObject{
    public EventClassTwo(Object source) {
        super(source);
    }
}
/**事件源類。表明誰觸發了事件,用於作為EventObject類的構造引數,在listener中作路由*/
class EventSource{
    private String who;
    Vector listeners=new Vector();   
    public EventSource(String who){
        this.who=who;
    }
    public String getActioner(){
        return who;
    }
    public void addMyEventListener(MyEventListener listener){
        listeners.add(listener);
    }
    /*設定say方法能被MyEventListener物件監聽到*/
    public void say(String words){
        System.out.println(this.getActioner()+"說:"+words);
        for(int i=0;i<listeners.size();i++){
            MyEventListener listener=(MyEventListener) listeners.elementAt(i);
            /*釋出事件。當然應該事先規劃say方法事件能釋出給哪些事件監聽器。*/
            listener.onMyEvent(new EventClassOne(this));
        }
    }
}
/**
 * 自定義監聽器中通過EventObject判斷事件來源,所以前面說EventObject是起路由功能。
 */
class MyEventListener implements EventListener{
    /*EventListener是與EventObject同級的最原始的監聽器,當然裡面什麼方法都沒有*/
    public void onMyEvent(EventObject e){
        /*如果該類與EventObject例項處於同一個類中,可以直接使用==判斷事件來源*/
        if(e.getSource() instanceof EventSource){
            /*事件來源於OtherSource時要處理的業務*/
            EventSource tempSrc=(EventSource)e.getSource();
            System.out.println("收到來自"+tempSrc.getActioner()+"的事件!");
        }
        /*else if(e.getSource() instanceof OtherSource){
            System.out.println("事件來源於OtherSource時要處理的業務");
        }*/
    }
}
/**
 * 關鍵點:new幾個事件,在awt、swing等介面程式設計中,ActionEvent事件(即行為)
 * 事件是java已經實現的,如點選滑鼠事件,所以元件A只需使用addXxxListener方法
 * 新增監聽器即可,執行時使用者點選元件A,則自動使用A產生一個相應事件,並執行所有
 * listener的actionPerformed方法處理事件業務。*/
public class Main{
    public static MyEventListener listener = null;
    public static void main(String[] args){
    
        listener = new MyEventListener();
        EventSource 小白 = new EventSource("小白");
        小白.addMyEventListener(listener);
        小白.say("今天天氣不錯");
        小白.say("適合出去走走");
    }
}


執行結果:


特別說明:

instanceof 操作符能辨別子類,如上面程式碼中MyEventListener 中原來為:

if(e.getSource() instanceof EventSource)
改為下列通過類判斷後同樣能得到正確結果:
if(e instanceof EventClassOne)