本文主要討論Java中的GUI圖形庫之一:SWT/JFace。在本文的第一部分,將解釋什麼是SWT/JFace以及如何安裝SWT/JFace。在本文的第二部分將以實例的方式討論如何使用SWT/JFace編寫GUI程序。
一、進入SWT/JFace世界
1.什麼是SWT/JFace
Java是一種強大的編程語言。但強大就意味復雜,尤其是和Java相關的名詞就象天上的星星一樣,數都數不過來。在本文中就涉及到兩個比較常用的名詞SWT和JFace。在標題中將SWT和JFace放到一起,並不是說SWT和JFace是一個意思,而是說它們的關系非常緊密。
基於Java的圖形庫最主要的有三種,它們分別是Swing、AWT和SWT。其中前兩個是Sun隨JDK一起發布的,而SWT則是由IBM領導的開源項目(現在已經脫離IBM了)Eclipse的一個子項目。SWT的執行效率非常高。這是由於SWT的底層是由C編寫的。由於SWT通過C直接調用系統層的GUI API。因此,使用SWT編寫GUI程序,在外觀上就和使用C++、Delphi(在Windows下)編寫的程序完全一樣。它的這一點和AWT類似。AWT在底層也是使用C直接調用系統層的GUI API。但它們是有區別的,最大的區別可能就是一個是Sun提供的,一個是Eclipse自帶的。這就意味著如果使用AWT,只要機器上安裝了JDK或JRE,發布軟件時無需帶其它的庫。而如何使用SWT,在發布時必須要自帶上SWT的*.dll(Windows版)或*.so(Linux/Unix版)文件以及相關的*.jar包。還有就是它們所提供的圖形接口有一些差異。SWT可能更豐富一些,我們可以看看Eclipse的界面就知道了。但隨著Sun對AWT庫的不斷更新,AWT的圖形表現能力也在不斷地提高。
雖然SWT很強大,但它比較底層。也就是說它的一些功能在使用上還比較低級,不太符合面向對象的特征。因此,在SWT的基礎上又開發了JFace。JFace在SWT上進行了一定的擴展。因此,也可說JFace是基於SWT的,就象在VC中使用MFC來包裝Win32 API一樣。
2.SWT/Face的安裝
在發布使用SWT/JFace編寫的GUI程序時,要隨程序帶上相應的庫文件。對於Windows版的SWT來說,SWT包含有4個dll文件和一個jar文件。它們是swt-awt-win32-3305.dll、swt-gdip-win32-3305.dll、swt-wgl-win32-3305.dll、swt-win32-3305.dll和swt.jar。在發布時,要將4個dll文件放到path路徑中,或者使用-Djava.library.path設置相應的路徑。將swt.jar放到classpath路徑中,或使用-classpath設置相應的jar包。而對於JFace,除了上述的5個文件外,還要帶上5個jar包:
org.eclipse.core.runtime_3.1.2.jar
org.eclipse.jface_3.1.1.jar
org.eclipse.jface.text_3.1.2.jar
org.eclipse.osgi_3.1.2.jar
org.eclipse.text_3.1.1.jar
這5個jar包都可以在eclipse的plugins目錄中找到,在這5個文件後面的版本號可能會因為eclipse的版本號不同而不同,但前面的部分都是一樣的。讀者在找這些jar包時應注意這一點。
SWT的開發包可以從http://www.eclipse.org單獨下載,也可以從eclipse的plugins目錄復制。而JFace的開發包並未提供單獨的下載,因此,JFace的開發包必須要從plugins目錄得到。
二、讓我們編寫第一個程序吧
學習一種新技術的最好方法就是去使用它。下面就讓我們來使用SWT和JFace來分別實現同一個程序。這個程序是一個簡單的記事本程序。在上面有三個按紐,分別是"新鍵"、"打開","保存",下面是一個文本框,用於編輯文本信息。下面讓我們先來看一下使用SWT實現的程序界面:
圖1 使用SWT實現的記事本程序界面
怎麼樣,看看上面的界面是不是和用Delphi、VC做的界面完全一樣!!
1.用SWT實現
不論一個程序帶不帶GUI,都必須有一個入口點,對於Java來說,這個入口點就是main函數。因此,在編寫程序之前,我們必須定義一個類,並且這個類中必須有個main函數。
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.*;
import org.eclipse.swt.events.*;
import java.io.*;
public class FirstSWT
{
// 用於記錄是否已經打開或保存了一個文件,如果已經打開或保存了一個文件,
// 這個變量就是這個文件的名子
private static String fn = "";
public static void main(String[] args)
{
… …
}
}
上面四個import將導入一些在本程序中要用到的jar包,前三個是SWT的包,最後一個是Java的標准輸入輸出包。
1、建立窗體
任何一個GUI程序,都至少有一個窗體(在本程序中只有一個窗體)。因此,下面我們就在main函數中建立這個窗體。
display = new Display();
shell = new Shell(display, SWT.DIALOG_TRIM);
shell.setText("第一個SWT程序");
shell.setSize(400, 300);
在上面4行代碼中涉及到了兩個類:Display和Shell。這兩個類都是在FirstSWT中定義的私有靜態類,之所以定義成全局的,是因為在以後的按鈕事件類中要使用它們。下面是它們的定義:
private static Display display;
private static Shell shell;
後面2條語句通過調用Shell類的setText和setSize方法,設置了窗口的標題和尺寸。
下面解釋一下Display和Shell類是什麼。
SWT在底層實現上分為兩層:系統層和用戶層。系統層就是直接和操作系統平台打交道,系統層的存在依賴於操作系統平台。在這裡,系統層就是Display類。Display的功能就是在系統和用戶之間架起一座橋梁,也就是說使用戶訪問系統資源透明化。而Shell類是直接和用戶打交道,因此,它屬於用戶層。通過Shell類可以控制窗體中的控件、窗體本身的屬性等。而Shell通過Display這座橋梁訪問系統級API。
l 向窗體中添加控件
接下來我們先在這個窗體上建立三個按鈕,代碼如下:
Button newButton = new Button(shell, SWT.PUSH);
newButton.setLocation(2, 5);
newButton.setSize(50, 20);
newButton.setText("新建");
Button openButton = new Button(shell, SWT.PUSH);
openButton.setLocation(60, 5);
openButton.setSize(50, 20);
openButton.setText("打開");
Button saveButton = new Button(shell, SWT.PUSH);
saveButton.setLocation(118, 5);
saveButton.setSize(50, 20);
saveButton.setText("保存");
按鈕類是Button,在建立時,Button需要兩個參數,一個是Shell對象,另外一個是按鈕的類型,在本例中,我們使用SWT.PUSH類型(一般的按鈕類型)。
注:和SWT相關的常量都定義在了SWT 中。
後面3條語句分別設置了三個按鈕的位置,尺寸和按鈕標題。
最後在3個按鈕下方建立一個文本框
text = new Text(shell, SWT.MULTI | SWT.BORDER | SWT.V_SCROLL | SWT.WRAP);
text.setLocation(2, 30);
text.setSize(shell.getClientArea().width - 4, shell.getClientArea().height - 35);
文本框的類是Text,和按鈕不同的是,由於文本框需要在按鈕事件中被訪問,因此,文本對象必須定義成全局的。
private static Text text;
1、添加控件事件代碼
現在讓我們為三個按鈕控件中加入事件代碼。和大多數語言不同的是,按鈕的單擊事件不叫Click,而叫Selection。一般需要將Selection事件代碼放到一個從SelectionAdapter類繼承的子類中。然後通過按鈕類的addSelectionListener方法將這個事件類的實例傳入按鈕類的實例中。但為了簡便起見,我們使用隱式建立對象的方法來建立事件類的對象。下面是"新建"按鈕的事件代碼。
newButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
fn = "";
shell.setText("第一個SWT程序");
text.setText("");
}
});
由於SelectionAdapter是一個抽象類,它有一個抽象方法widgetSelected,在上述代碼被override了。在"新建"按鈕中將全局文件名賦成空串,並將窗體的標題賦成初始狀態,最後將文本框清空。
接下來讓我們看看"打開"按鈕的事件代碼:
openButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
FileDialog dlg = new FileDialog(shell, SWT.OPEN);
String fileName = dlg.open();
try
{
if (fileName != null)
{
// 打開指定的文件
FileInputStream fis = new FileInputStream(fileName);
text.setText("");
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
String s = null;
// 將指定的文件一行一行地加到文本框中
while ((s = in.readLine()) != null)
text.append(s + "\r\n");
}
if (fileName != null)
{
fn = fileName;
shell.setText(fn);
MessageBox successBox = new MessageBox(shell);
successBox.setText("信息");
successBox.setMessage("打開文件成功!");
successBox.open();
}
}
catch (Exception e)
{
MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR);
errorBox.setText("錯誤");
errorBox.setMessage("打開文件失敗!");
errorBox.open();
}
}
});
上面代碼的基本邏輯是使用打開對話框選擇一個文件,使用FileInputStream將這個文件打開,並且將文件中的內容一行一行地加入到文本框中,如果這個過程失敗,顯示錯識對話框,如果成功,將fn變量和窗體的標題欄都賦成這個文件名。
最後讓我們實現"保存"按鈕事件的代碼。
saveButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent event)
{
try
{
String fileName = null;
if (fn.equals(""))
{
FileDialog dlg = new FileDialog(shell, SWT.SAVE);
fileName = dlg.open();
if(fileName != null)
fn = fileName;
}
if (fn != "")
{
FileOutputStream fos = new FileOutputStream(fn);
OutputStreamWriter out = new OutputStreamWriter(fos);
out.write(text.getText());
out.close();
shell.setText(fn);
MessageBox successBox = new MessageBox(shell);
successBox.setText("信息");
successBox.setMessage("保存文件成功!");
successBox.open();
}
}
catch (Exception e)
{
MessageBox errorBox = new MessageBox(shell, SWT.ICON_ERROR);
errorBox.setText("錯誤");
errorBox.setMessage("保存文件失敗!");
errorBox.open();
}
}
});
這段代碼的基本邏輯是如果當前已經打開一個文件或已經將當前的新建文件保存過了,在點擊"保存"按鈕時,不再顯示保存對話框,而直接將文件保存,否則,將顯示一個保存對話框,通過這個對話框可以選擇一個文件名,然後再保存。
1、讓我們最後畫龍點睛吧
程序到這已經基本完成了,但還需要進行最後一步,就是對事件進行監聽。在main函數的最後,可以加上如下的代碼實現這個功能。
shell.open(); // 顯示窗體
while (!shell.isDisposed()) // 當窗體未被關閉時執行循環體內的代碼
{
// 如果未發生事件,通過sleep方法進行監視事件隊列
if (!display.readAndDispatch())
{
display.sleep();
}
}
display.dispose(); // 釋放底層的資源
看看上面的代碼,是不是有點象MFC的監聽事件代碼!!
2、用JFace實現
使用JFace實現GUI程序和SWT的最大的區別就是JFace的窗體類必須從ApplicationWindow繼承。
import org.eclipse.jface.window.ApplicationWindow;
public class FirstJFace extends ApplicationWindow
{
public static void main(String[] args)
{
… …
}
}
另外一個不同是在設置窗體上,JFace通過ApplicationWindow類提供一系列的事件函數,通過在這些函數中編寫代碼,可以很方便地對窗體進行操作。如可以在createContents函數中向窗體中加入控件。
protected Control createContents(Composite parent)
{
// 這裡邊的代碼就是上述的建立控件的代碼,只是要將shell換成parent
}
由於使用JFace操作控件的方式和SWT類似,本文將不再詳細討論,感性趣的讀者可以參考本文提供的源代碼。使用JFace的程序界面和SWT完全一樣,界面如圖2所示:
圖2使用JFace實現的記事本程序界面
本文配套源碼