我的JAVA窩! - BlogJava

          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 都有一個 XyzAdapter adapter 都是抽象類并且實現了對應的 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??????*?@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 ,它們分別是對應于窗口事件( ShellListener ShellAdapter )的 ShellEvent ,對應于鍵盤事件 (KeyListener KeyAdapter) KeyEvent 和對應于鼠標事件( MouseListener MouseAdapter )的 MouseEvent 。希望可以起到窺一斑而見全豹的作用。

          幾種 EventObject 簡介

          ShellEvent

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

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

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

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

          KeyEvent

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

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

          keyCode 是我們稱為鍵碼的東西,什么是鍵碼呢?如果你打開 org.eclipse.swt.SWT API 文檔,你會發現里面有很多都和鍵盤有關的整型常量,比如 SWT.F1 SWT.F4 SWT.ESC SWT.KEYPAD_3 之類,這就是他們的鍵碼。

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

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

          MouseEvent

          MouseEvent 對應于的是鼠標事件。它同樣包含四個屬性: button stateMask x y

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

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

          x y 指的是相對于部件的橫坐標和縱坐標。

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

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

          Untyped Events

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

          所謂的 untyped events 你可以理解為一個事件的大雜燴。和 untyped event 相聯系的兩個類是 Listener Event 。在這里我想請大家注意一下,這兩個類不是在 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 構筑起一個非常完美的程序來。

          posted on 2006-12-18 00:21 J2S 閱讀(158) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2006年12月>
          262728293012
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          導航

          統計

          常用鏈接

          留言簿(2)

          隨筆檔案

          最新隨筆

          搜索

          積分與排名

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 扎赉特旗| 常山县| 新沂市| 共和县| 通城县| 新绛县| 广南县| 谷城县| 子长县| 芒康县| 西畴县| 民勤县| 浑源县| 舞阳县| 洛川县| 张家口市| 巴彦县| 抚顺市| 桃园市| 礼泉县| 越西县| 武鸣县| 京山县| 海门市| 青河县| 昭平县| 滦南县| 雅江县| 磐石市| 临夏县| 招远市| 宁晋县| 宁远县| 石城县| 白朗县| 高平市| 呼伦贝尔市| 桓台县| 娄底市| 思南县| 沅陵县|