SWT/JFace開發入門指南(四)

讓你的swt程序動起來

在向使用者提供最差的用戶體驗方面,中國的IT企業始終走在時代的最前端。之所以有這樣的感慨其實是來源於往blog上貼上一節的內容:我用了一整天的功夫,不斷與CSDN各種莫名其妙的出錯提示進行鬥爭,最後終於成功的貼了上去。

 

 

 

 

 

 

 

其實作爲CSDN blog一個使用者,我的要求並不高:只要能寫blog,能夠正常訪問就可以了。然而就是這麼一點基本的要求好像也得不到滿足。

 

 

 

 

 

 

 

我不知道大家有沒有這樣的體驗:其實軟件使用者要求的東西都很基本,而現在軟件做得越來越複雜,有相當大一部分是在於軟件開發者把自己的注意力放在了一些附加功能(這些功能可能讓用戶感到驚喜,但是如果沒有它們用戶也不會不滿意)上,而真正用戶的要求卻得不到滿足。所以大家在設計程序的時候,一定要明白,有時候簡單就是一種美,把時間花費到真正有價值的地方去。

 

 

 

 

 

 

 

OK,回到我們的主題上來。在這一節中,我將給大家介紹swt的事件模式。在前面我們也提到過,寫一個swt程序,無非就是分幾步走。其中比較需要費心的就是佈置好用戶界面和處理各種事件。

 

 

 

 

 

 

 

添加了事件處理的Hello,world!

 

其實swt中處理事件非常簡單,對應於各種事件都有相應的listener類,如果一種事件叫做Xyz,那麼對應的listener類就是XyzListener。比如對應於鼠標事件的有MouseListener,對應於鍵盤事件的就是KeyListener。而在每種widget中,對於它可以處理的事件都有addXyzListener方法,只要把對應的listener實例作爲參數傳給它就可以了。

 

 

 

 

 

 

 

爲了更加清楚的說明,我們先來看下面一段程序:

 

 

 

 

 

 

 

 1 public class EventDemo {
 2 
 3     private Shell _shell;
 4 
 5     public EventDemo() {
 6         Display display = new Display();
 7         Shell shell = new Shell(display,SWT.SHELL_TRIM);
 8         setShell(shell);
 9         RowLayout layout=new RowLayout();
10         shell.setLayout(layout);
11         shell.setText("Event demo");
12         
13         Button button=new Button(shell,SWT.PUSH | SWT.CENTER);
14         button.setText("Click me!");
15 
16         button.addSelectionListener(new SelectionListener(){
17 
18             public void widgetSelected(SelectionEvent event) {
19                 handleSelectionEvent();
20             }
21 
22             public void widgetDefaultSelected(SelectionEvent event) {
23             }     
24         });
25         shell.setBounds(200,300,100,100);
26         shell.open();
27 
28         while (!shell.isDisposed()) {
29             if (!display.readAndDispatch()) {
30                 display.sleep();
31             }
32         }
33         display.dispose();    
34         
35     }
36 
37     protected void handleSelectionEvent() {
38         MessageBox dialog=new MessageBox(getShell(),SWT.OK|SWT.ICON_INFORMATION);
39         dialog.setText("Hello");
40         dialog.setMessage("Hello,world!");
41         dialog.open();
42     }
43 
44     /**
45      * @param args
46      */
47     public static void main(String[] args) {
48 
49         EventDemo eventdemo=new EventDemo();
50     }
51 
52     /**
53      * @return Returns the _shell.
54      */
55     public Shell getShell() {
56         return _shell;
57     }
58 
59     /**
60      * @param _shell The _shell to set.
61      */
62     public void setShell(Shell shell) {
63         this._shell =shell;
64     }
65 }
66 

代碼段 6

 

 

 

 

 

 

 

你可以看到在這段程序中,我們只創建了一個Button,隨後調用了它的addSelectionListener()方法,在這個新創建的Listener,我們只爲widgetSelected方法添加了代碼,並在其中創建了一個對話框。實際運行效果如下圖,其中那個標有Hello,world的對話框是按了按鈕以後出現的:

 

 

 

 

 

 

 

 

 

6

 

 

 

 

 

 

 

如果總結一下,我們可以得出處理事件的幾個步驟:

 

 

 

 

 

 

 

1.      針對你所處理的事件,找出合適的XyzListener接口

 

 

 

 

 

 

 

2.      編寫一個新的類,這個類實現了XyzListener接口

 

 

 

 

 

 

 

3.      在你所感興趣的事件中編寫處理代碼,而對於那些你不感興趣的方法可以讓它們保持空白(就像實例中的widgetDefaultSelected()方法)一樣

 

 

 

 

 

 

 

讓事件處理更加簡單:使用適配器(adapter

 

有時候我們可能會感覺這樣仍然不夠簡單,比如我只對SelectionListener中的widgetSelected()方法感興趣,但是爲了能夠通過編譯器的編譯,我卻不得不寫一個空白的widgetDefaultSelected()方法(因爲SelectionListener是一個接口,你必須實現它所有的方法)。

 

 

 

 

 

 

 

幸運的是,swt幫我們解決了這個問題,途徑就是使用adapter。在swt中,對應於一個XyzListener都有一個XyzAdapteradapter都是抽象類並且實現了對應的listener接口,它爲對應listener接口中的每個方法都定義了一個默認實現(基本上就是什麼都不做),我們在使用時候只需要override掉自己感興趣的方法就可以了。

 

 

 

 

 

 

 

結合上一小節中的代碼,如果使用SelectionAdapter代替SelectionListener的話,我們的代碼就可以這樣寫:

 

 

 

 

 

 

 

button.addSelectionListener(new SelectionAdapter(){
            
public void widgetSelected(SelectionEvent event) {
                handleSelectionEvent();
            }
        });

 

這樣是不是很方便呢?

 

 

 

 

 

 

 

EventObject事件處理的附加信息

 

在處理各種事件時,我們需要一些附加信息,而EventObject給我們提供了這些信息。

 

 

 

 

 

 

 

我們先來看下面這個簡單的小程序,在這段程序中,我們創建了兩個文本框,當在第一個文本框輸入時,第二個文本框中顯示輸入的字符。

 

 

 

 

 

 

 

 1 public class EventDemo2 {
 2 
 3     Text logText;
 4     
 5     public EventDemo2() {
 6         Display display = new Display();
 7         Shell shell = new Shell(display,SWT.SHELL_TRIM);
 8         
 9         GridLayout layout=new GridLayout();
10         layout.numColumns=2;
11         shell.setLayout(layout);
12         shell.setText("Event demo");
13         
14         Label label1=new Label(shell,SWT.RIGHT);
15         label1.setText("text1:");
16         Text text1=new Text(shell,SWT.NONE);
17         
18         text1.addKeyListener(new KeyAdapter(){
19             public void keyPressed(KeyEvent e) {
20                 Text t=getLogText();
21                 String s=t.getText();
22                 t.setText(String.valueOf(e.character));
23             } 
24         }
25         );
26         
27         Label label2=new Label(shell,SWT.RIGHT);
28         label2.setText("text2:");
29         Text text2=new Text(shell,SWT.NONE);
30         text2.setEditable(false);
31         text2.setBackground(new Color(display,255,255,255));
32         setLogText(text2);
33         
34         shell.pack();
35         shell.open();
36 
37         while (!shell.isDisposed()) {
38             if (!display.readAndDispatch()) {
39                 display.sleep();
40             }
41         }
42         display.dispose();           
43     }
44     /**
45      * @param args
46      */
47     public static void main(String[] args) {
48         EventDemo2 demo2=new EventDemo2();
49     }
50     /**
51      * @return Returns the logText.
52      */
53     public Text getLogText() {
54         return logText;
55     }
56     /**
57 
SWT/JFace開發入門指南(四)(續)
 
56     /**
57      * @param logText The logText to set.
58      */
59     public void setLogText(Text logText) {
60         this.logText = logText;
61     }
62 }
63 

代碼段 7

 

 

 

 

 

 

 

你可能沒有興趣仔細研究這麼長的代碼,那麼讓我們只關注這一小段代碼:

 

 

 

 

 

 

 

 text1.addKeyListener(new KeyAdapter(){
            
public void keyPressed(KeyEvent e) {
                Text t
=getLogText();
                String s
=t.getText();
                t.setText(String.valueOf(e.character));
            } 
        }
        );

在這段代碼中,我們使用了KeyAdapter來處理鍵盤事件,而keyPressed會在有鍵按下時候被調用,我們在函數中使用了KeyEvent類型的參數e,並且通過e.character得到了按下鍵對應的字符。

 

 

 

 

 

 

 

各種EventObject(例如上面示例中的KeyEvent)在事件處理函數中作爲參數出現,它們可能有不同的屬性和方法,利用這些特性我們可以做很多有意思的事情。

 

 

 

 

 

 

 

我們下面只簡單介紹幾種EventObject,它們分別是對應於窗口事件(ShellListenerShellAdapter)的ShellEvent,對應於鍵盤事件(KeyListenerKeyAdapter)KeyEvent和對應於鼠標事件(MouseListenerMouseAdapter)的MouseEvent。希望可以起到窺一斑而見全豹的作用。

 

 

 

 

 

 

 

幾種EventObject簡介

ShellEvent

如果你打開ShellEventAPI,你會很驚訝的發現它只有一個布爾型的屬性,就是doit。這個莫名其妙的屬性是用來做什麼的呢?

 

 

 

 

 

 

 

我們知道,Shell對應的就是程序的窗口,在ShellListener中定義的幾種事件包括窗口激活時候的shellActivated,窗口即將被關閉時候的shellClosed等等。ShellEvent中唯一的屬性doit,就是用來設定是否這些動作將有效的。

 

 

 

 

 

 

 

再說得具體一些,比如Windows下通常我們會通過點擊窗口右上角的關閉按鈕來關閉窗口,這個時候就會對shellClosed進行調用,如果我們在shellClosed(ShellEvent e)方法中把ShellEvent對象edoit屬性置爲了false,那麼這次動作就無效,窗口不會被關閉。

 

 

 

 

 

 

 

在有些其他的EventObject中也有doit屬性,它們的作用都是類似的。比如KeyEvent就有這樣的一個屬性。如果你在keyPressed方法中把它置爲了false,就等於你按鍵盤(對於對應的widget,也就是receiver來講)沒有用。

 

 

 

 

 

 

 

KeyEvent

其實在前面我們或多或少的已經介紹了一些KeyEvent的知識。KeyEvent包含四個屬性:characterdoitkeyCodestateMask

 

 

 

 

 

 

 

其中character我們在前面的示例中使用過,它其實就是按鍵對應字符,而doitShellEvent中的doit含義是相同的。

 

 

 

 

 

 

 

keyCode是我們稱爲鍵碼的東西,什麼是鍵碼呢?如果你打開org.eclipse.swt.SWTAPI文檔,你會發現裏面有很多都和鍵盤有關的整型常量,比如SWT.F1SWT.F4SWT.ESCSWT.KEYPAD_3之類,這就是他們的鍵碼。

 

 

 

 

 

 

 

stateMask則是用來檢測AltShiftCtrl這些鍵有沒有同時被按下。

 

 

 

 

 

 

 

stateMask與這些鍵的鍵碼進行位與運算,如果得到的結果不是0就說明這些鍵被按下了,比如如果stateMake & SWT.ALT不爲零,我們就可以認爲Alt鍵被按下了。

 

 

 

 

 

 

 

MouseEvent

MouseEvent對應於的是鼠標事件。它同樣包含四個屬性:buttonstateMaskxy

 

 

 

 

 

 

 

button就是說明按下的是哪個鍵,比如對於普通鼠標來說,1是左鍵,2是右鍵等等

 

 

 

 

 

 

 

stateMask卻是用來反映鍵盤的狀態的,這和KeyEvent中的stateMask含義是相同的。

 

 

 

 

 

 

 

xy指的是相對於部件的橫座標和縱座標。

 

 

 

 

 

 

 

你可能會覺得有點疑問,光是這麼一點屬性就能處理鼠標事件了麼?如果我有一個滾輪鼠標,那應該用什麼事件處理滾輪的動作呢?答案是:目前可能還無法利用事件模式處理,關於這一點可以參照一下這個url: https://bugs.eclipse.org/bugs/show_bug.cgi?id=58656

 

 

 

 

 

 

 

關於EventObject我就只介紹到這裏,這當然很不夠,但是我強烈建議大家在實際應用中多查閱eclipseswt的相關文檔。因爲畢竟精力有限,我的目的是讓大家通過這篇文章能夠找到一個正確獲取知識的方向,而不是把這些知識很詳細的介紹給大家。

 

 

 

 

 

 

 

Untyped Events

我們在這裏提到了untyped events,那肯定就有typed eventtypeduntyped本身並不是說事件有什麼不一樣,而是說事件處理是使用了特定的Listener還是沒有。我們前面提到的所有事件處理都是typed 類型,因爲它們都使用了特定Listener

 

 

 

 

 

 

 

所謂的untyped events你可以理解爲一個事件的大雜燴。和untyped event相聯繫的兩個類是ListenerEvent。在這裏我想請大家注意一下,這兩個類不是在org.eclipse.swt.events中,而是在org.eclipse.swt.widgets中。

 

 

 

 

 

 

 

Listener只有一個方法handleEvent,這個方法裏你可以處理任何事件。而如果你打開Event看一下,就能看到我們剛剛在前一小節中介紹過的那些XxxEvent中的屬性在這裏應有盡有。所以它可以起到替代它們的作用,當然如果是一個窗口被關閉的事件,相信你用keyCode屬性意義不大。

 

 

 

 

 

 

 

讓我們看一下下面一段代碼

 

 

 

 

 

 

 

 1 Shell shell = new Shell ();
 2    Listener listener = new Listener () {
 3       public void handleEvent (Event e) {
 4          switch (e.type) {
 5             case SWT.Resize:
 6                System.out.println ("Resize received");
 7                break;
 8             case SWT.Paint:
 9                System.out.println ("Paint received");
10                break;
11             default:
12                System.out.println ("Unknown event received");
13          }
14       }
15    };
16    shell.addListener (SWT.Resize, listener);
17    shell.addListener (SWT.Paint, listener);
18 

代碼段 8

 

 

 

 

 

 

 

由此我們大體上可以體會到untyped events的處理方式。

 

 

 

 

 

 

 

小結

關於事件處理,我就向大家介紹這麼多。到現在爲止,我們已經基本上可以寫一些簡單的swt用戶交互程序了。然而這還遠遠不夠,畢竟人們總是希望有更華麗(或者說:豐富)的界面,讓用戶能夠獲得更好的體驗。在下一節中,我計劃和大家討論一些這樣的部件。

 

 

 

 

 

 

 

另外可能你覺得有些疑惑,爲什麼寫了這麼多內容,都是關於swt的呢?Jface的內容呢?我的計劃是在大部分swt有關的內容介紹完了以後再向大家介紹Jface。事實上,即使不用Jface,你也完全可以用swt構築起一個非常完美的程序來。

 

 

 

 

 

 

 

最後,給自己做一個小廣告,因爲CSDNblog功能實在太令人失望了,所以我希望在近期內可以把blog遷移到blogjava.net,具體地址是:http://www.blogjava.net/jayliu,歡迎大家提出寶貴意見。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章