從零開始理解JAVA事件處理機制(3)


我們連續寫了兩小節的教師-學生的例子,必然覺得無聊死了,這樣的例子我們就是玩上100遍,還是不知道該怎么寫真實的代碼。那從本節開始,我們開始往真實代碼上面去靠攏。

事件最容易理解的例子是鼠標事件:我們點擊鼠標,鼠標發送指令,執行代碼。

 

一:鼠標點擊事件處理模型基礎版

這個時候,我們必須去查看下JDK中相關類型。對照着上一節《從零開始理解JAVA事件處理機制(2)》中的UML圖,我們很快發現,對應HomeworkListener,JDK中有MouseListener,其實我們靠分析也能得知,MouseListener繼承自EventListener。現在既然有了接口MouseListener了,那我們必定會有一個實現類,這個類假設叫做:ConcreteMouseListener。不妨先實現之:

package com.zuikc.events;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

public class ConcreteMouseListener implements MouseListener {

    public ConcreteMouseListener(){

    }
    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.printf("我被{%s}點了一下,MD癢死了~~", e.getSource().toString());
    }

    @Override
    public void mousePressed(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // TODO Auto-generated method stub
    }

    @Override
    public void mouseExited(MouseEvent e) {
        // TODO Auto-generated method stub
    }

}

我們為單擊的事件處理器添加業務代碼。

事件處理器:監聽器的具體實現類的實現方法,就叫事件處理器。

接下來要看什么,當然是MouseEvent。MouseEvent,這個JDK中的類相對來說,就稍微有點大了,起構造方法的參數有點多,不過沒有關系呀,我們慢慢看,我先說這個類要怎么用,即怎么new出來。

/*
* 這里的new Component() {} 就是 event.getSource() 得到的事件源 source
*/
MouseEvent event = new MouseEvent(new Component() {}, 1, 1, 1,2,3,4,false);

在實際且正常的情況下,MouseEvent是沒有必要自己new的,JAVA運行時會捕獲硬件鼠標的點擊動作,由虛擬機底層為我們生成該實例對象(下文會為我們分析這一點),但是我們此時此刻我們是先模擬呀,所以不妨礙我們自己胡亂new一個出來。注意,new不是問題,問題的關鍵我們必須知道其構造器參數的意義,而其中核心關鍵參數就是第一個參數,new Component(),這是什么?這就是那個事件源!回頭看看我們的教師學生版本是在哪里生產事件的:

    public void setHomework(String homework) {
        System.out.printf("%s布置了作業%s \n", this.name, homework);
        homeworks.add(homework);
        HomeworkEventObject event = new HomeworkEventObject(this);
        /*
         * 在觀察者模式中,我們直接調用Observable的notifyObservers來通知被觀察者
         * 現在我們只能自己通知了~~
         */
        for (HomeworkListener listener : homeworkListenerList) {
            listener.update(event, homework);
        }

    }

是在Teacher的業務代碼setHomework中。所以,在當前的我們要寫的這個例子中,new MouseEvent要在哪里呢?我們在Button的業務代碼中,Button是誰,Button就類似Teacher,但又不完全等同Teacher,在Teacher中,Teacher本身就是事件源,所以它這個this作為參數傳入進了HomeworkEventObject,而Button不能作為參數傳入進MouseEvent,因為我不打算讓Button繼承自Component,所以我們先new了一個臨時的Component。OK,分析到了這里,我們自己的Button代碼大概就出來了,是這個樣子的:

package com.zuikc.events;

import java.awt.AWTEvent;
import java.awt.AWTEventMulticaster;
import java.awt.Component;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.peer.LightweightPeer;

public class Button {
    private MouseListener mouseListener;
    public void addMouseListener(MouseListener l) {
        mouseListener = l;
    }
    public void doClick(){
        /*
         * 這里的new Component() {} 就是 event.getSource() 得到的事件源 source
         */
        MouseEvent event = new MouseEvent(new Component() {}, 1, 1, 1,2,3,4,false);
        //event.getSource();
        this.mouseListener.mouseClicked(event);
    }
}

至此,我們可以畫出清晰的類圖了,來看:

image

順便我們看一下Client端的代碼:

public static void main(String[] args) {
    ConcreteMouseListener listener = new ConcreteMouseListener();
    Button button = new Button();
    button.addMouseListener(listener);
    button.doClick();
}

運行一下吧,你應該得到一句類似這樣的輸出:

我被{com.zuikc.events.Button$1[,0,0,0x0,invalid]}點了一下,MD癢死了~~

 

二,一個正常的窗體程序的樣子

上面,我們盡量偏向於教師學生的例子,寫出了鼠標事件的基礎版本,但是說好的程序本來的樣子呢?來,我們接下來寫個正常的程序,99.9%在人在寫窗體程序的時候就是如下這么寫的。我知道你們又會有人上來罵了,什么,java,窗體程序?我TMD學JAVA是為了EE開發的,企業開發的。現在,我們先說好不好互相傷害,要知道,即便是NB如JAVA,最先也是先從窗體發跡的,並且,JAVA的窗體框架推倒重寫了還不止一次。所以,窗體的事件你明白了,EE中那些框架的事件碰到了簡直跟切白菜一樣。

言歸正傳,看代碼:

package com.zuikc.events;

import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JFrame;

public class Client {
    public static void main(String[] args) {
        new DemoFrame();
    }
}

class DemoFrame extends JFrame implements MouseListener {

    public DemoFrame() {
        super("demo");
        this.setSize(500, 400);
        this.setLocationRelativeTo(null);
        this.getContentPane().setLayout(null);
        this.setVisible(true);

        JButton button1 = new JButton("ok");
        button1.setBounds(8, 8, 80, 80);
        button1.addMouseListener(this);
        this.getContentPane().add(button1);
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        System.out.printf("我被{%s}點了一下,MD癢死了~~", e.getSource().toString());
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}

這段代碼什么意思?最簡單了,就是創建了一個窗體,窗體上放置了一個按鈕,點擊了之后,執行了一行代碼。這簡簡單單的一個文件,沒多少行代碼,實際上就實現了我們上文中一堆類中實現的功能。來吧,我們分析吧,把監聽器、事件處理器、事件、事件源都指出來。

監聽器:DemoFrame就是監聽器,對應ConcreteMouseListener;

事件處理器:MouseClicked方法就是監聽器,ConcreteMouseListener里面也有這個方法;

事件:看不到了,怎么辦?

事件源:看不到了,怎么辦?

注意,窗體本身就監聽器,所以上文代碼中為button添加監聽器怎么做?button1.addMouseListener(this);沒錯,就是把自身添加進去。

然后,事件和事件源都看不到了,這個時候怎么辦?我們如果看輸出的話,上文代碼的輸出為:

我被{javax.swing.JButton[,8,8,80x80,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@7fda7dfe,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=ok,defaultCapable=true]}點了一下,MD癢死了~~

看上去,類似我們上文第一部分代碼的輸出,也是JButton業務代碼運行過程中生成的一個變量,但它是在哪里生成的,在哪里產生的,我們並不知道。不過沒關系,我們看調試堆棧!

一步步的往上追,我們終於追到了這里:

 

image

由此可見,MouseEvent也是在業務代碼里new出來了,大家可能要為,那這個重要的第一個參數target呢?target可是事件源也很重要,道理很簡單,往上繼續追,限於篇幅,這里不在展開,它在某個你願意看到它的地方被new出來了。

現在我們補齊回答,

事件:JAVA運行時捕獲到硬件鼠標觸發,從而調用了事件處理器,在事件處理器內部生成的這個MouseEvent,就是事件;

事件源:JAVA運行時捕獲到硬件鼠標觸發,從而調用了事件處理器,在事件處理器內部生成的這個target,就是事件源;

 

三:正常版的上文第一部分的代碼

按照二中的代碼來寫,我們第一部分的代碼應該是什么樣子的呢?

一和二放在一起比較,其實只要改兩個地方,一中的代碼就和二中完全一致了,

1:將ConcreteMouseListener命名為DemoFrame;

2:將Button實例由客戶端放置到ConcreteMouseListener內部;

OK,事件就是這么簡單。

該系列的第一部分和第二部分分別在:

1:從零開始理解JAVA事件處理機制(1)

2:從零開始理解JAVA事件處理機制(2)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM