# 13.4 捕獲事件
大家可注意到假如編譯和運行上面的程序片,按下按鈕后不會發生任何事情。必須進入程序片內部,編寫用于決定要發生什么事情的代碼。對于由事件驅動的程序設計,它的基本目標就是用代碼捕獲發生的事件,并由代碼對那些事件作出響應。事實上,GUI的大部分內容都是圍繞這種事件驅動的程序設計展開的。
經過本書前面的學習,大家應該有了面向對象程序設計的一些基礎,此時可能會想到應當有一些面向對象的方法來專門控制事件。例如,也許不得不繼承每個按鈕,并重載一些“按鈕按下”方法(盡管這顯得非常麻煩有有限)。大家也可能認為存在一些主控“事件”類,其中為希望響應的每個事件都包含了一個方法。
在對象以前,事件控制的典型方式是`switch`語句。每個事件都對應一個獨一無二的整數編號;而且在主事件控制方法中,需要專門為那個值寫一個`switch`。
Java 1.0的AWT沒有采用任何面向對象的手段。此外,它也沒有使用`switch`語句,沒有打算依靠那些分配給事件的數字。相反,我們必須創建`if`語句的一個嵌套系列。通過if語句,我們需要嘗試做的事情是偵測到作為事件“目標”的對象。換言之,那是我們關心的全部內容——假如某個按鈕是一個事件的目標,那么它肯定是一次鼠標點擊,并要基于那個假設繼續下去。但是,事件里也可能包含了其他信息。例如,假如想調查一次鼠標點擊的像素位置,以便畫一條引向那個位置的線,那么`Event`對象里就會包含那個位置的信息(也要注意Java 1.0的組件只能產生有限種類的事件,而Java 1.1和Swing/JFC組件則可產生完整的一系列事件)。
Java 1.0版的AWT方法串聯的條件語句中存在`action()`方法的調用。雖然整個Java 1.0版的事件模型不兼容Java 1.1版,但它在還不支持Java1.1版的機器和運行簡單的程序片的系統中更廣泛地使用,忠告您使用它會變得非常的舒適,包括對下面使用的`action()`程序方法而言。
`action()`擁有兩個參數:第一個是事件的類型,包括所有的觸發調用`action()`的事件的有關信息。例如鼠標單擊、普通按鍵按下或釋放、特殊按鍵按下或釋放、鼠標移動或者拖動、事件組件得到或丟失焦點,等等。第二個參數通常是我們忽略的事件目標。第二個參數封裝在事件目標中,所以它像一個參數一樣的冗長。
需調用`action()`時情況非常有限:將控件置入窗體時,一些類型的控件(按鈕、復選框、下拉列表單、菜單)會發生一種“標準行動”,從而隨相應的`Event`對象發起對`action()`的調用。比如對按鈕來說,一旦按鈕被按下,而且沒有再多按一次,就會調用它的`action()`方法。這種行為通常正是我們所希望的,因為這正是我們對一個按鈕正常觀感。但正如本章后面要講到的那樣,還可通過`handleEvent()`方法來處理其他許多類型的事件。
前面的例程可進行一些擴展,以便象下面這樣控制按鈕的點擊:
```
//: Button2.java
// Capturing button presses
import java.awt.*;
import java.applet.*;
public class Button2 extends Applet {
Button
b1 = new Button("Button 1"),
b2 = new Button("Button 2");
public void init() {
add(b1);
add(b2);
}
public boolean action(Event evt, Object arg) {
if(evt.target.equals(b1))
getAppletContext().showStatus("Button 1");
else if(evt.target.equals(b2))
getAppletContext().showStatus("Button 2");
// Let the base class handle it:
else
return super.action(evt, arg);
return true; // We've handled it here
}
} ///:~
```
為了解目標是什么,需要向`Event`對象詢問它的`target`(目標)成員是什么,然后用`equals()`方法檢查它是否與自己感興趣的目標對象引用相符。為所有感興趣的對象寫好引用后,必須在末尾的`else`語句中調用`super.action(evt, arg)`方法。我們在第7章已經說過(有關多態性的那一章),此時調用的是我們重載過的方法,而非它的基類版本。然而,基類版本也針對我們不感興趣的所有情況提供了相應的控制代碼。除非明確進行,否則它們是不會得到調用的。返回值指出我們是否已經處理了它,所以假如確實與一個事件相符,就應返回`true`;否則就返回由基類`event()`返回的東西。
對這個例子來說,最簡單的行動就是打印出到底是什么按鈕被按下。一些系統允許你彈出一個小消息窗口,但Java程序片卻防礙窗口的彈出。不過我們可以用調用`Applet`方法的`getAppletContext()`來訪問瀏覽器,然后用`showStatus()`在瀏覽器窗口底部的狀態欄上顯示一條信息(注釋③)。還可用同樣的方法打印出對事件的一段完整說明文字,方法是調用`getAppletConext().showStatus(evt + "")`。空字符串會強制編譯器將`evt`轉換成一個字符串。這些報告對于測試和調試特別有用,因為瀏覽器可能會覆蓋我們的消息。
③:`ShowStatus()`也屬于`Applet`的一個方法,所以可直接調用它,不必調用`getAppletContext()`。
盡管看起來似乎很奇怪,但我們確實也能通過`event()`中的第二個參數將一個事件與按鈕上的文字相配。采用這種方法,上面的例子就變成了:
```
//: Button3.java
// Matching events on button text
import java.awt.*;
import java.applet.*;
public class Button3 extends Applet {
Button
b1 = new Button("Button 1"),
b2 = new Button("Button 2");
public void init() {
add(b1);
add(b2);
}
public boolean action (Event evt, Object arg) {
if(arg.equals("Button 1"))
getAppletContext().showStatus("Button 1");
else if(arg.equals("Button 2"))
getAppletContext().showStatus("Button 2");
// Let the base class handle it:
else
return super.action(evt, arg);
return true; // We've handled it here
}
} ///:~
```
很難確切知道`equals()`方法在這兒要做什么。這種方法有一個很大的問題,就是開始使用這個新技術的Java程序員至少需要花費一個受挫折的時期來在比較按鈕上的文字時發現他們要么大寫了要么寫錯了(我就有這種經驗)。同樣,如果我們改變了按鈕上的文字,程序代碼將不再工作(但我們不會得到任何編譯時和運行時的信息)。所以如果可能,我們就得避免使用這種方法。
- Java 編程思想
- 寫在前面的話
- 引言
- 第1章 對象入門
- 1.1 抽象的進步
- 1.2 對象的接口
- 1.3 實現方案的隱藏
- 1.4 方案的重復使用
- 1.5 繼承:重新使用接口
- 1.6 多態對象的互換使用
- 1.7 對象的創建和存在時間
- 1.8 異常控制:解決錯誤
- 1.9 多線程
- 1.10 永久性
- 1.11 Java和因特網
- 1.12 分析和設計
- 1.13 Java還是C++
- 第2章 一切都是對象
- 2.1 用引用操縱對象
- 2.2 所有對象都必須創建
- 2.3 絕對不要清除對象
- 2.4 新建數據類型:類
- 2.5 方法、參數和返回值
- 2.6 構建Java程序
- 2.7 我們的第一個Java程序
- 2.8 注釋和嵌入文檔
- 2.9 編碼樣式
- 2.10 總結
- 2.11 練習
- 第3章 控制程序流程
- 3.1 使用Java運算符
- 3.2 執行控制
- 3.3 總結
- 3.4 練習
- 第4章 初始化和清除
- 4.1 用構造器自動初始化
- 4.2 方法重載
- 4.3 清除:收尾和垃圾收集
- 4.4 成員初始化
- 4.5 數組初始化
- 4.6 總結
- 4.7 練習
- 第5章 隱藏實現過程
- 5.1 包:庫單元
- 5.2 Java訪問指示符
- 5.3 接口與實現
- 5.4 類訪問
- 5.5 總結
- 5.6 練習
- 第6章 類復用
- 6.1 組合的語法
- 6.2 繼承的語法
- 6.3 組合與繼承的結合
- 6.4 到底選擇組合還是繼承
- 6.5 protected
- 6.6 累積開發
- 6.7 向上轉換
- 6.8 final關鍵字
- 6.9 初始化和類裝載
- 6.10 總結
- 6.11 練習
- 第7章 多態性
- 7.1 向上轉換
- 7.2 深入理解
- 7.3 覆蓋與重載
- 7.4 抽象類和方法
- 7.5 接口
- 7.6 內部類
- 7.7 構造器和多態性
- 7.8 通過繼承進行設計
- 7.9 總結
- 7.10 練習
- 第8章 對象的容納
- 8.1 數組
- 8.2 集合
- 8.3 枚舉器(迭代器)
- 8.4 集合的類型
- 8.5 排序
- 8.6 通用集合庫
- 8.7 新集合
- 8.8 總結
- 8.9 練習
- 第9章 異常差錯控制
- 9.1 基本異常
- 9.2 異常的捕獲
- 9.3 標準Java異常
- 9.4 創建自己的異常
- 9.5 異常的限制
- 9.6 用finally清除
- 9.7 構造器
- 9.8 異常匹配
- 9.9 總結
- 9.10 練習
- 第10章 Java IO系統
- 10.1 輸入和輸出
- 10.2 增添屬性和有用的接口
- 10.3 本身的缺陷:RandomAccessFile
- 10.4 File類
- 10.5 IO流的典型應用
- 10.6 StreamTokenizer
- 10.7 Java 1.1的IO流
- 10.8 壓縮
- 10.9 對象序列化
- 10.10 總結
- 10.11 練習
- 第11章 運行期類型識別
- 11.1 對RTTI的需要
- 11.2 RTTI語法
- 11.3 反射:運行期類信息
- 11.4 總結
- 11.5 練習
- 第12章 傳遞和返回對象
- 12.1 傳遞引用
- 12.2 制作本地副本
- 12.3 克隆的控制
- 12.4 只讀類
- 12.5 總結
- 12.6 練習
- 第13章 創建窗口和程序片
- 13.1 為何要用AWT?
- 13.2 基本程序片
- 13.3 制作按鈕
- 13.4 捕獲事件
- 13.5 文本字段
- 13.6 文本區域
- 13.7 標簽
- 13.8 復選框
- 13.9 單選鈕
- 13.10 下拉列表
- 13.11 列表框
- 13.12 布局的控制
- 13.13 action的替代品
- 13.14 程序片的局限
- 13.15 視窗化應用
- 13.16 新型AWT
- 13.17 Java 1.1用戶接口API
- 13.18 可視編程和Beans
- 13.19 Swing入門
- 13.20 總結
- 13.21 練習
- 第14章 多線程
- 14.1 反應靈敏的用戶界面
- 14.2 共享有限的資源
- 14.3 堵塞
- 14.4 優先級
- 14.5 回顧runnable
- 14.6 總結
- 14.7 練習
- 第15章 網絡編程
- 15.1 機器的標識
- 15.2 套接字
- 15.3 服務多個客戶
- 15.4 數據報
- 15.5 一個Web應用
- 15.6 Java與CGI的溝通
- 15.7 用JDBC連接數據庫
- 15.8 遠程方法
- 15.9 總結
- 15.10 練習
- 第16章 設計模式
- 16.1 模式的概念
- 16.2 觀察器模式
- 16.3 模擬垃圾回收站
- 16.4 改進設計
- 16.5 抽象的應用
- 16.6 多重分發
- 16.7 訪問器模式
- 16.8 RTTI真的有害嗎
- 16.9 總結
- 16.10 練習
- 第17章 項目
- 17.1 文字處理
- 17.2 方法查找工具
- 17.3 復雜性理論
- 17.4 總結
- 17.5 練習
- 附錄A 使用非JAVA代碼
- 附錄B 對比C++和Java
- 附錄C Java編程規則
- 附錄D 性能
- 附錄E 關于垃圾收集的一些話
- 附錄F 推薦讀物