在窗口系統(tǒng)中,程序一般都是以事件驅(qū)動的。SWT的Control類實現(xiàn)了一些事件監(jiān)聽的注冊方法,其子類都可以通過這些方法注冊鼠標(biāo)和鍵盤的監(jiān)聽器,通過實現(xiàn)這些監(jiān)聽器的接口,窗口組件就能響應(yīng)相應(yīng)的鼠標(biāo)和鍵盤事件。
Control類是窗口組件類的基類,它定義了基本的事件監(jiān)聽方法,有如下一些:
addControlListener
addFocusListener
addHelpListener
addKeyListener
addMouseListener
addMouseTrackListener
addMouseMoveListener
addPaintListener
addTraverseListener
添加事件監(jiān)聽器步驟如下:
1. 創(chuàng)建事件監(jiān)聽器。例如:“new KeyListener()…”。
2. 通過Control類的子類添加事件監(jiān)聽器。例如:“text.addKeyListener()”。
常用事件有鼠標(biāo)事件、鍵盤事件、焦點事件、窗口控制事件和選擇事件,Control類的子類也可以定義添加相關(guān)的監(jiān)聽方法。
鼠標(biāo)事件
在窗口系統(tǒng)中,鼠標(biāo)基本上是必備的設(shè)備。一般來說,窗口中鼠標(biāo)有鼠標(biāo)單擊、鼠標(biāo)雙擊、鼠標(biāo)進入窗口、鼠標(biāo)退出窗口及鼠標(biāo)移動等事件。
在SWT中,事件的響應(yīng)是通過相應(yīng)接口實現(xiàn)的,每個組件可以添加相應(yīng)的事件響應(yīng)實例來監(jiān)聽事件。例如“button.addMouseListener(listener)”,表示在button組件上添加鼠標(biāo)的相應(yīng)響應(yīng)事件實例,其中,listener為實現(xiàn)監(jiān)聽器對象,下面就具體的鼠標(biāo)事件進行介紹。
MouseListener接口
在SWT中,通過實現(xiàn)MouseListener接口來響應(yīng)鼠標(biāo)的按下、松開及雙擊事件,MouseListener接口如例程1所示。
例程1 MouseListener.java
package org.eclipse.swt.events;
import org.eclipse.swt.internal.SWTEventListener;
public interface MouseListener extends SWTEventListener {
public void mouseDoubleClick(MouseEvent e);
public void mouseDown(MouseEvent e);
public void mouseUp(MouseEvent e);
}
其中,mouseDoubleClick表示鼠標(biāo)雙擊事件響應(yīng)方法,mouseDown表示鼠標(biāo)鍵按下事件的響應(yīng)方法,mouseUp表示鼠標(biāo)鍵起來事件的響應(yīng)方法,MouseEvent為系統(tǒng)傳入的鼠標(biāo)事件的參數(shù),MouseEvent中的button屬性表示鼠標(biāo)的按鈕值,例如e.button等于1,表示鼠標(biāo)左鍵按下,按鈕值對應(yīng)鼠標(biāo)按鈕如表1所示。
在程序中,開發(fā)人員可以根據(jù)e.button的值判斷當(dāng)前用戶按的是哪一個鼠標(biāo)鍵,從而確定采用什么操作。
MouseMoveListener接口
在SWT中,通過實現(xiàn)MouseMoveListener接口來響應(yīng)鼠標(biāo)的移動事件,MouseMoveListener接口如例程2所示。
例程2 MouseMoveListener.java
package org.eclipse.swt.events;
import org.eclipse.swt.internal.SWTEventListener;
public interface MouseMoveListener extends SWTEventListener {
public void mouseMove(MouseEvent e);
}
MouseMoveListener只有mouseMove方法,用來響應(yīng)窗口中鼠標(biāo)移動事件。
MouseTrackListener接口
在SWT中,通過實現(xiàn)MouseTrackListener接口來響應(yīng)鼠標(biāo)進入窗口、鼠標(biāo)退出窗口和鼠標(biāo)停放在窗口上的事件,MouseTrackListener接口如例程3所示。
例程3 MouseTrackListener.java
package org.eclipse.swt.events;
import org.eclipse.swt.internal.SWTEventListener;
public interface MouseTrackListener extends SWTEventListener {
public void mouseEnter(MouseEvent e);
public void mouseExit(MouseEvent e);
public void mouseHover(MouseEvent e);
}
其中,mouseEnter表示鼠標(biāo)進入窗口事件的響應(yīng)方法,mouseExit表示鼠標(biāo)退出窗口事件的響應(yīng)方法,mouseHover表示鼠標(biāo)停放在窗口上事件的響應(yīng)方法。
鼠標(biāo)事件實例
為了更深入地理解鼠標(biāo)事件,下面通過具體的實例演示如何響應(yīng)鼠標(biāo)事件。該程序只是當(dāng)事件觸發(fā)時簡單地打印出相應(yīng)信息,在具體的實例中,讀者可以根據(jù)需要進行修改,代碼如例程4所示。
例程4 MouseEventExample.java
/**
* 為了節(jié)省篇幅,所有的import類已經(jīng)被注釋
* 讀者可以通過ctrl+shift+o快捷鍵,自動引入所依賴的類
* 如果有問題可發(fā)郵件到ganshm@gmail.com
* */
public class MouseEventExample implements MouseListener, MouseMoveListener,
MouseTrackListener {
//顯示信息的標(biāo)簽
Label myLabel = null;
Shell shell = null;
public MouseEventExample() {
}
public void run() {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout());
shell.setSize(450, 200);
shell.setText("Mouse Event Example");
myLabel = new Label(shell, SWT.BORDER);
myLabel.setText("I ain't afraid of any old mouse");
//在當(dāng)前窗口上添加鼠標(biāo)響應(yīng)事件
shell.addMouseListener(this);
shell.addMouseMoveListener(this);
shell.addMouseTrackListener(this);
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
public static void main(String[] args) {
new MouseEventExample().run();
}
public void mouseDoubleClick(MouseEvent e) {
myLabel.setText("Double Click " + e.button + " at: " + e.x + "," + e.y);
System.out.println("Double Click " + e.button + " at: " + e.x + ","
+ e.y);
}
public void mouseDown(MouseEvent e) {
myLabel.setText("Button " + e.button + " Down at: " + e.x + "," + e.y);
System.out.println("Button " + e.button + " Down at: " + e.x + ","
+ e.y);
}
public void mouseUp(MouseEvent e) {
myLabel.setText("Button " + e.button + " Up at: " + e.x + "," + e.y);
System.out.println("Button " + e.button + " Up at: " + e.x + "," + e.y);
}
public void mouseMove(MouseEvent e) {
myLabel.setText("Mouse Move at: " + e.x + "," + e.y);
System.out.println("Mouse Move at: " + e.x + "," + e.y);
}
public void mouseEnter(MouseEvent e) {
myLabel.setText("Mouse Enter at: " + e.x + "," + e.y);
System.out.println("Mouse Enter at: " + e.x + "," + e.y);
}
public void mouseExit(MouseEvent e) {
myLabel.setText("Mouse Exit at: " + e.x + "," + e.y);
System.out.println("Mouse Exit at: " + e.x + "," + e.y);
}
public void mouseHover(MouseEvent e) {
myLabel.setText("Mouse Hover at: " + e.x + "," + e.y);
System.out.println("Mouse Hover at: " + e.x + "," + e.y);
}
}
MouseEventExample類實現(xiàn)了MouseListener、MouseMoveListener和MouseTrackListener 3個接口,并通過“shell.addMouseListener(this);”、“shell.addMouseMoveListener(this);”和“shell.addMouseTrackListener(this);”把自己作為監(jiān)聽器添加到了窗口中,程序運行效果如圖1所示。

圖1 鼠標(biāo)事件實例
鍵盤事件
鍵盤事件是最簡單,也是最常用的事件。一般來說,鍵盤事件有兩種:鍵按下和鍵松開。SWT通過KeyListener響應(yīng)鍵盤事件。
KeyListener接口
在SWT中,通過實現(xiàn)KeyListener接口來響應(yīng)鍵按下和松開的事件。KeyListener接口如例程5所示。
例程5 KeyListener.java
package org.eclipse.swt.events;
import org.eclipse.swt.internal.SWTEventListener;
public interface KeyListener extends SWTEventListener {
public void keyPressed(KeyEvent e);
public void keyReleased(KeyEvent e);
}
其中,keyPressed表示鍵按下事件的響應(yīng)方法,keyReleased表示鍵松開事件的響應(yīng)方法,KeyEvent為系統(tǒng)傳入的鍵盤事件的參數(shù),用戶可以通過KeyEvent參數(shù)找到相應(yīng)的按鍵值。
鍵盤事件實例
為了更深入地了解鍵盤事件,下面通過具體的實例演示如何響應(yīng)鍵盤事件。該程序只是當(dāng)事件觸發(fā)時簡單地打印出相應(yīng)信息,在具體的實例中,讀者可以根據(jù)需要進行修改,代碼如例程6所示。
例程6 KeyListenerExample.java
public class KeyListenerExample {
Display display;
Shell shell;
KeyListenerExample() {
display = new Display();
shell = new Shell(display);
shell.setSize(250, 200);
shell.setText("A KeyListener Example");
Text text = new Text(shell, SWT.BORDER);
text.setBounds(50 ,50 ,100, 20);
text.addKeyListener(new KeyListener() {
public void keyPressed(KeyEvent e) {
System.out.println("key Pressed -"+e.character);
}
public void keyReleased(KeyEvent e) {
System.out.println("key Released -"+e.character);
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
display.dispose();
}
public static void main(String[] args) {
new KeyListenerExample();
}
}
程序中通過匿名內(nèi)部類實現(xiàn)監(jiān)聽器,這種方式在實例開發(fā)中會經(jīng)常用到。另外可以通過KeyEvent取得按鍵的值,如上例的“e.character”得到按鍵的字符,程序運行效果如圖2所示。

圖2 鍵盤事件實例
焦點事件
在窗口系統(tǒng)中,當(dāng)組件獲得輸入焦點或失去焦點時將觸發(fā)相應(yīng)的事件。SWT通過FocusListener監(jiān)聽焦點事件。
FocusListener接口
在SWT中,通過實現(xiàn)FocusListener接口來響應(yīng)獲得焦點和失去焦點的事件。FocusListener接口如例程7所示。
例程7 FocusListener.java
package org.eclipse.swt.events;
import org.eclipse.swt.internal.SWTEventListener;
public interface FocusListener extends SWTEventListener {
public void focusGained(FocusEvent e);
public void focusLost(FocusEvent e);
}
其中,focusGained表示組件獲得焦點事件的響應(yīng)方法,focusLost表示組件失去焦點事件的響應(yīng)方法,F(xiàn)ocusEvent為系統(tǒng)傳入的焦點事件的參數(shù),用戶可以通過FocusEvent參數(shù)找到相應(yīng)的組件。
焦點事件實例
為了更深入地理解焦點事件,下面通過具體的實例演示如何響應(yīng)焦點事件。該程序只是簡單地改變當(dāng)前獲得焦點和失去焦點組件的顯示信息,在具體的實例中,讀者可以根據(jù)需要進行修改,代碼如例程8所示。
例程8 FocusListenerExample.java
public class FocusListenerExample {
public static void main(String[] args) {
Display display = new Display();
Shell shell = new Shell(display);
shell.setLayout(new GridLayout(3, true));
shell.setText("One Potato, Two Potato");
//新建焦點監(jiān)聽器
FocusListener listener = new FocusListener() {
public void focusGained(FocusEvent event) {
//獲得觸發(fā)事件的組件
Button button = (Button) event.getSource();
//焦點獲得時改變顯示文本
button.setText("I'm It!");
}
public void focusLost(FocusEvent event) {
//獲得觸發(fā)事件的組件
Button button = (Button) event.getSource();
//焦點獲得時改變顯示文本
button.setText("Pick Me!");
}
};
for (int i = 0; i < 6; i++) {
Button button = new Button(shell, SWT.PUSH);
button.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
button.setText("Pick Me!");
button.addFocusListener(listener);
}
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
程序中顯示了6個按鈕,并為按鈕添加上了焦點監(jiān)聽器,程序運行效果如圖3所示。

圖3 焦點事件實例
窗口控制事件
在窗口系統(tǒng)中,當(dāng)組件被移動或改變大小時將觸發(fā)相應(yīng)的事件。SWT通過ControlListener監(jiān)聽窗口控制事件。
ControlListener接口
在SWT中,通過實現(xiàn)ControlListener接口來響應(yīng)組件被移動或改變大小的事件。ControlListener接口如例程9所示。
例程9 ControlListener.java
package org.eclipse.swt.events;
import org.eclipse.swt.internal.SWTEventListener;
public interface ControlListener extends SWTEventListener {
public void controlMoved(ControlEvent e);
public void controlResized(ControlEvent e);
}
其中,controlMoved表示組件被移動事件的響應(yīng)方法,controlResized表示組件被改變大小事件的響應(yīng)方法,ControlEvent為系統(tǒng)傳入的窗口控制事件的參數(shù),用戶可以通過ControlEvent參數(shù)找到相應(yīng)的組件。
窗口控制事件實例
為了更深入地理解窗口控制事件,下面通過具體的實例演示如何響應(yīng)窗口控制事件。該程序只是簡單地打印組件被移動或改變大小的信息,在具體的實例中,讀者可以根據(jù)需要進行修改,代碼如例程10所示。
例程10 ControlListenerExample.java
public class ControlListenerExample {
public void run() {
Display display = new Display();
Shell shell = new Shell(display);
shell.addControlListener(new ControlListener() {
public void controlMoved(ControlEvent e) {
System.out.println("control move");
}
public void controlResized(ControlEvent e) {
System.out.println("control resize");
}
}
);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
public static void main(String[] args) {
new ControlListenerExample().run();
}
}
上例顯示的只是一個空白的窗口,當(dāng)用戶移動窗口或改變窗口大小時,將會觸發(fā)監(jiān)聽器所實現(xiàn)的事件。
選擇事件
SWT的很多組件都實現(xiàn)了選擇組件事件的監(jiān)聽機制,例如按鈕、菜單項的選擇,當(dāng)選擇了相關(guān)的菜單項或組件時,將觸發(fā)相應(yīng)的事件。
SelectionListener接口
在SWT中,通過實現(xiàn)SelectionListener接口來響應(yīng)選擇組件事件。SelectionListener接口如例程11所示。
例程11 SelectionListener.java
package org.eclipse.swt.events;
import org.eclipse.swt.internal.SWTEventListener;
public interface SelectionListener extends SWTEventListener {
public void widgetSelected(SelectionEvent e);
public void widgetDefaultSelected(SelectionEvent e);
}
其中,widgetSelected表示組件被選擇事件的響應(yīng)方法,widgetDefaultSelected表示組件默認(rèn)選擇事件的響應(yīng)方法,SelectionEvent為系統(tǒng)傳入的選擇事件的參數(shù)。
選擇組件事件實例
為了更深入地理解選擇組件事件,下面通過具體的實例演示如何響應(yīng)選擇組件事件。該程序只是簡單地打印組件被移動或改變大小的信息,在具體的實例中,讀者可以根據(jù)需要進行修改,代碼如例程12所示。
例程12 SelectonListenerExample.java
public class SelectonListenerExample {
public static void main(String[] args) {
Display display = new Display();
final Shell mainShell = new Shell(display);
Button button = new Button(mainShell, SWT.PUSH);
button.setText("Close Me!");
button.setBounds(10, 10, 100, 30);
// 添加選擇組件事件
button.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent event) {
System.out.println("select button");
mainShell.close();
}
public void widgetDefaultSelected(SelectionEvent e) {
// 不執(zhí)行任何操作
}
});
mainShell.open();
while (!mainShell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
}
上例中只是添加了一個按鈕,當(dāng)選擇按鈕時關(guān)閉當(dāng)前窗口。
其他常用事件
SWT中為了實現(xiàn)特定的功能,很多組件都提供了特定事件的響應(yīng),本節(jié)將通過實例介紹HelpListener、VerifyListener和ModifyListener 3個特定的事件監(jiān)聽器。
HelpListener、VerifyListener和ModifyListener監(jiān)聽器的功能
HelpListener監(jiān)聽器通過helpRequested(HelpEvent e)方法響應(yīng)用戶的幫助請求事件,當(dāng)用戶在組件獲得焦點后按【F1】鍵將觸發(fā)此事件。
VerifyListener監(jiān)聽器通過verifyText(VerifyEvent e)方法響應(yīng)校驗輸入事件。此監(jiān)聽器只對文本輸入校驗,當(dāng)用戶輸入了數(shù)據(jù)后,verifyText方法將通過設(shè)置VerifyEvent中的doit屬性判斷輸入是否正確,從而確定修改是否有效。doit屬性為true時修改有效,即“e.doit = true;”。
ModifyListener監(jiān)聽器通過modifyText(ModifyEvent e)方法響應(yīng)文本被修改的事件。此監(jiān)聽器只對文本輸入校驗。
提示:如果VerifyListener監(jiān)聽器和ModifyListener監(jiān)聽器同時存在的話,會先響應(yīng)校驗輸入事件,如果校驗成功再響應(yīng)修改事件。
HelpListener、VerifyListener和ModifyListener監(jiān)聽器實例
在此實例中,用戶可以輸入華氏溫度和攝氏溫度,通過監(jiān)聽器判斷輸入是否正確及計算相應(yīng)的攝氏溫度和華氏溫度,另外,還可以按【F1】鍵獲得當(dāng)前組件的信息,代碼如例程13所示。
例程13 MultipleListenersExample.java
public class MultipleListenersExample implements HelpListener, VerifyListener,
ModifyListener {
private static final double FIVE_NINTHS = 5.0 / 9.0;
private static final double NINE_FIFTHS = 9.0 / 5.0;
private Text fahrenheit;
private Text celsius;
private Label help;
public void run() {
Display display = new Display();
Shell shell = new Shell(display);
shell.setText("Temperatures");
createContents(shell);
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
private void createContents(Shell shell) {
shell.setLayout(new GridLayout(3, true));
new Label(shell, SWT.LEFT).setText("Fahrenheit:");
fahrenheit = new Text(shell, SWT.BORDER);
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 2;
fahrenheit.setLayoutData(data);
fahrenheit.setData("Type a temperature in Fahrenheit");
// 為華氏溫度文本框添加監(jiān)聽器
fahrenheit.addHelpListener(this);
fahrenheit.addVerifyListener(this);
fahrenheit.addModifyListener(this);
new Label(shell, SWT.LEFT).setText("Celsius:");
celsius = new Text(shell, SWT.BORDER);
data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 2;
celsius.setLayoutData(data);
celsius.setData("Type a temperature in Celsius");
//為攝氏溫度文本框添加監(jiān)聽器
celsius.addHelpListener(this);
celsius.addVerifyListener(this);
celsius.addModifyListener(this);
help = new Label(shell, SWT.LEFT | SWT.BORDER);
data = new GridData(GridData.FILL_HORIZONTAL);
data.horizontalSpan = 3;
help.setLayoutData(data);
}
//響應(yīng)幫助事件
public void helpRequested(HelpEvent event) {
help.setText((String) event.widget.getData());
}
//響應(yīng)校驗事件
public void verifyText(VerifyEvent event) {
event.doit = false;
char myChar = event.character;
String text = ((Text) event.widget).getText();
if (myChar == '-' && text.length() == 0) event.doit = true;
if (Character.isDigit(myChar)) event.doit = true;
if (myChar == '\b') event.doit = true;
}
//響應(yīng)文本修改的事件
public void modifyText(ModifyEvent event) {
// 刪除監(jiān)聽器,從而在modifyText過程中不會觸發(fā)事件
celsius.removeVerifyListener(this);
celsius.removeModifyListener(this);
fahrenheit.removeVerifyListener(this);
fahrenheit.removeModifyListener(this);
Text text = (Text) event.widget;
try {
int temp = Integer.parseInt(text.getText());
if (text == fahrenheit) {
celsius.setText(String.valueOf((int) (FIVE_NINTHS * (temp - 32))));
} else {
fahrenheit.setText(String.valueOf((int) (NINE_FIFTHS * temp + 32)));
}
} catch (NumberFormatException e) { /* Ignore */ }
//添加監(jiān)聽器
celsius.addVerifyListener(this);
celsius.addModifyListener(this);
fahrenheit.addVerifyListener(this);
fahrenheit.addModifyListener(this);
}
public static void main(String[] args) {
new MultipleListenersExample().run();
}
}
程序運行效果如圖4所示。

圖4 文本監(jiān)聽器
提示:一般來說,監(jiān)聽器都有一個抽象的Adaper類實現(xiàn)監(jiān)聽器的方法,例如FocusAdapter實現(xiàn)了FocusListener的方法(方法為空)。如果讀者不想實現(xiàn)監(jiān)聽器的全部方法則可以繼承監(jiān)聽器的Adaper類,否則要實現(xiàn)監(jiān)聽器接口的所有方法。