但是很多軟件學習者在學習可視化開發的過程中,只是非常表面的來理解可視化編程,他們可能認為能夠使用拖放方式完成一個界面就非常值得稱道,但是很少有人會認真的去理解編程語言對於可視化編程組件的支持和整合,在Softworks軟件人才培訓中心的兩年教學過程,我深刻的感受到了這一點,因此下文將會結合我的教學經驗來講解可視化編程過程中最為關鍵的“事件驅動模型”。
1.什麼是事件驅動模型?
在講解事件驅動模型之前,我們現在看看事件驅動模型的三大要素:
·事件源:能夠接收外部事件的源體。
·偵聽器:能夠接收事件源通知的對象。
·事件處理程序:用於處理事件的對象。
學員應該要理解任何基於事件驅動模型的開發技術都包含以上三大要素,不管是.Net還是Java技術,甚至是以前我們使用的Visual Basic和Delphi語言都有基於以上三大要素的事件驅動模型開發流程。
現在我們來看一個生活中的示例,如果有一天你走在路上一不小心被天上掉下來的花瓶砸到了,並且暈死了過去。那麼整個過程其實就是一個事件處理流程,而且我們可以非常方便的分析出剛才所提到的事件驅動模型中的三大要素。
1.被砸暈的這個人其實就是事件源,因為他是能夠接受到外部的事件的源體。
2.偵聽器就是這個人的大腦神經,因為它會感知到疼痛。
3.事件處理就是這個人暈死了過去。
由於事件驅動模型在我們日常生活中是無處不在的,因此Java和其他的編程語言都將這一過程運用到了可視化編程中了。
2.Java編程語言中的事件驅動模型
在Java編程技術中,最常用的可視化編程當屬Java Swing技術,Java Swing為開發者提供了很多現成的組件,如:按鈕(JButton),單選按鈕(JRadioButton)等。為了管理用戶與組成程序圖形用戶界面的組件間的交互,必須理解在Java中如何處理事件。
假設用戶單擊了程序圖形用戶界面中的一個按鈕,其實該按鈕就是這個事件的源(可以引發事件的物體)。所有的Java Swing對象都有感知自己被操作的能力,因此JButton按鈕也具有這樣能力。一個事件通常必須有一個源對象,這裡就是JButton對象。當單擊按鈕時,JButton組件類會生成一個用於存放該事件參數的ActionEvent的對象,該對象包含了事件及事件源的信息。圖1-1顯示了這種機制。
圖 1-1
當JButton感知到自己被點擊以後會將這種感覺傳遞給某個偵聽器對象,該偵聽器對象原先已被告知對該類事件感興趣,偵聽器對象僅是一種偵聽特定事件的對象。這裡的“將事件傳遞給偵聽器”僅意味著事件源調用偵聽器對象中的一個特定方法,並以事件對象作為實參。偵聽器對象可以偵聽一個特定對象的事件(比如一個按鈕)。
其實可以使任何類的對象成為偵聽器對象,只要使該類實現偵聽器接口。你將會發現有各種各樣的偵聽器接口,以滿足不同類型事件的需要。在這個單擊按鈕的例子中,需要實現ActionListener接口以便接收按鈕事件。在偵聽器接口聲明的方法中,實現了接受這個事件對象並響應該事件的代碼。在本例中,在事件發生時,調用了ActionListener接口中的actionPerformed()方法。每種偵聽器接口都定義了特定的方法,用來接收該偵聽器計劃要處理的事件。
僅僅實現偵聽器接口還不足以將偵聽器對象連接到事件源上,仍需要把偵聽器與希望處理的事件單個源或多個源連接起來。通過調用事件源對象的特定方法,可以注冊帶有事件源的偵聽器對象。例如,為了注冊偵聽單擊按鈕事件的偵聽器,需要調用JButton對象的addActionListener()方法,該操作可以使偵聽對象和事件源綁定。
每個事件響應時只涉及到對該事件感興趣的偵聽器。由於偵聽器只要求實現一個合適的接口,所以實際上,可以在任何希望的地方接收和處理事件。在Java中使用偵聽器對象處理事件的方式,稱為委托事件模型,這是因為對於諸如按鈕這種組件引起的事件響應,並不是由引起事件的對象本身處理,而是委托獨立的偵聽器對象進行處理,剛才的actionPerformed()其實就是一個委托處理方法。現在讓我們來看一下,JButton是如何將用戶的點擊轉化成方法處理的(如圖1-2)。
圖1-2
JButton組件初始化代碼片斷:
private void initialize() {
frame = new JFrame();
frame.getContentPane ().setLayout (null);
frame.setBounds (100, 100, 247, 165);
frame.setDefaultCloSEOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle ("事件驅動程序");
//btnPress就是這次點擊操作中的事件源
btnPress = new JButton();
btnPress.setText ("Press");
btnPress.setName ("Press");
btnPress.setBounds (63, 98, 99, 23);
//向事件源btnPress植入偵聽器對象ButtonEventHandler
btnPress.addActionListener (newButtonEventHandler(this));
frame.getContentPane ().add(btnPress);
frame.getContentPane ().add(txtMessage);
}
偵聽器創建的代碼片斷:
//偵聽器對象ButtonEventHandler(用來偵聽按鈕的點擊操作)
class ButtonEventHandler implements ActionListener {
//窗體對象
private EventDemo form = null;
//通過構造體傳入窗體對象,
//作用在於讓偵聽器對象明白事件源處於
//哪個窗體容器中
public ButtonEventHandler(EventDemo form) {
this.form = form;
}
//委托方法
public void actionPerformed(ActionEvent e) {
//該方法將會把事件的處理權交給窗體容器類的
//btnPress_Click方法處理。
this.form.btnPress_Click(e);
}
}
* 按鈕btnPress的事件處理方法。
*
* @param e 事件參數
*/
private void btnPress_Click(ActionEvent e) {
String message = "你點擊的按鈕名叫:"
+ ((JButton) e.getSource()).getName();
this.txtMessage.setText(message);
}
代碼工作原理:
JButton組件初始化代碼片斷已經明確闡述了按鈕被創建後放置於窗體上,關鍵在於本代碼片斷的以下語句:
btnPress.addActionListener(new ButtonEventHandler(this));
該語句就是向事件源植入了偵聽器對象ButtonEventHandler,該類實現了ActionListener結構,因此JButton類的對象btnPress這個時候已經具有了處理用戶點擊按鈕的能力了。
當用戶點擊btnPress這個按鈕的時候,按鈕對象會直接把這次點擊感覺傳遞給ButtonEventHandler的actionPerformed方法處理,為養成較好的編程習慣,我們中心並不建議學員直接在該委托方法中編寫代碼,而是需要將該事件處理再次轉發給窗體中的某個方法來處理,這個方法的命名也必須是有規則的,就是事件源名+下劃線+事件名(btnPress_Click),並且該方法必須具有事件參數ActionEvent,因為在該對象中明確指明了,哪個按鈕受到了點擊了。e.getSource()方法返回了被點擊按鈕的對象,由於這次被點擊的是一個按鈕,因此我們需要使用JButton對e.getSource()的返回值進行強轉,隨後通過getName()方法得到這個按鈕的名字。至此整個點擊事件處理完了。