上一篇博客介紹了NetBeans Java代碼編輯器,這一篇將介紹用NetBeans 開發(fā)基于Swing Application Framework (JSR 296) 的程序,Swing一直以來是飽受爭議的GUI庫,特別是SWT與Swing的爭論,分別體現(xiàn)在了Eclipse與NetBeans的爭論上。
在此,我無心討論SWT與Swing孰優(yōu)孰劣,你要是真的對這個感興趣,可以到williamchen的博客里的Swing專欄看看,該專欄博主已經(jīng)對SWT和Swing分析得非常透徹:
http://blog.sina.com.cn/swingjava
Swing在1998年末作為Java 2的一部分發(fā)布了1.0,但是當時發(fā)布的版本不管是性能還是外觀都足以令人失望。雖然當時Swing小組的工程師使用了最前沿的技術,如設計模式,但是時的Swing小組已經(jīng)被性能、本地化外觀一致性問題以及許多錯誤深深陷住了,這些問題幾乎把整個項目拖垮。
幸虧,Swing小組堅持了下來,要不然今天我們就不會看到像NetBeans這樣的IDE了。自從JDK1.5后,Java的性能已經(jīng)有了巨大的改觀,現(xiàn)在1.6下,Swing的性能已經(jīng)完全不是問題。
那么Swing的優(yōu)勢有那些呢?
l 優(yōu)秀的設計,正如剛才所說的,Swing從九十年代中期開始設計就用了當時最前沿的開發(fā)方法學。可以說Swing在當今各種GUI庫中,設計是非常優(yōu)雅的。
l 輕量級
l 官方的存在JRE中,發(fā)布程序不需要像SWT那樣還要捆綁自己的GUI庫
l 真正的平臺獨立,Swing組件由Java 2D繪制
l 輕易的更改面觀模式
l 強大的IDE支持,現(xiàn)在用NetBeans做界面,幾乎不用你敲一行代碼。
l Swing現(xiàn)在又多了一個殺手級框架Swing Application Framework (JSR 296)
下面開始我的第二篇Java博客
NetBeans 6.0 提高生產(chǎn)力之Swing Application Framework (JSR 296)
Swing Application Framework簡介
Swing Application Framework(以下簡稱SAF)致力于簡化Swing應用程序的開發(fā),框架定義了對大多數(shù)桌面應用程序的共有的基礎設施:
l 應用程序生命周期管理,特別是GUI的啟動和關閉。
l 對資源管理和載入的支持,這些資源是字符串,格式化的消息,圖像,顏色,字體以及桌面應用程序共有的其他類型的資源。
l 對動作定義,管理和綁定的支持,其中包含了異步運行的動作(在后臺運行)。
l 持久化會話狀態(tài):支持自動地,有選擇地保存應用程序一次運行到下次運行的GUI狀態(tài)。比如頂層的窗口幾何位置。
用NetBeans開發(fā)SAF
SAF已經(jīng)定義了大多數(shù)桌面應用程序的基礎設施,我們可以更方便的開發(fā)Swing程序了,但是手動編碼開發(fā)GUI還是讓很多人抓狂。能不能提供一個像VB一樣的環(huán)境,讓程序員更加專注于功能的實現(xiàn)而不是界面的實現(xiàn)?答案是肯定的,NetBeans 6.0就為開發(fā)SAF提供了這樣一個環(huán)境,甚至比VB還要做得更好。
那么到底NetBeans6.0為SAF提供了什么樣的優(yōu)勢呢?
l 生成基礎框架,不必每次新創(chuàng)建一個程序的時候都從頭開始
l 唾手可得的國際化支持,在Netbeans里對SAF程序國際化,簡單得不能再簡單
l 擁有Swing世界里的明星級UI設計工具模塊Matisse提供的免費大餐
l 用NetBeans開發(fā)SAF,意味著你還可以使用Beans Binding(JSR 295),數(shù)據(jù)綁定將問題變得更簡單
開始之前
用NetBeans 6.0開發(fā)基于SAF,其實不用明白SAF,主要遵循一定的開發(fā)原則也可以開發(fā)出穩(wěn)定高效的SAF程序來
但是為了你更深入的理解SAF,強烈建議看下面的文章
l Using the Swing Application Framework (JSR 296)
http://java.sun.com/developer/technicalArticles/javase/swingappfr/
l Swing應用程序框架(Swing Application Framework)API緒論(JSR-296)之一
http://www.javaeye.com/topic/81326
Swing應用程序框架(Swing Application Framework)API緒論(JSR-296)之二
http://www.javaeye.com/topic/81327
l 如果你有更多的時間,那么請看下面網(wǎng)址有關Swing的部分
http://blog.sina.com.cn/swingjava
開發(fā)環(huán)境準備
JDK1.6或以上版本
NetBeans6.0或以上版本
提示:Netbeans 網(wǎng)站上提供有六種捆綁包下載
l Web & Java EE
l Mobility
l Java SE
l Ruby
l C/C++
l All
開發(fā)Swing Application Framework只需要Java SE下載包就行了
這樣能真正發(fā)揮NetBeans的性能!
創(chuàng)建項目
我們新建一個項目,從Java種類里選擇 Java Desktop Application,如下圖

點擊下一步,接著輸入項目名稱、項目存放地址和程序的主類
在Choose Application Shell里面我們選擇Basic Application,單擊完成

提示:我們?nèi)绻麆?chuàng)建基于Swing的數(shù)據(jù)庫程序,那么選擇BataBase Application將提供極大的便利。但是在這里我只是介紹Basic Application,也就是現(xiàn)在我們用SAF開發(fā)普通的桌面程序。
生成的項目目錄結(jié)構(gòu)

l META-INF/services目錄
該目錄一般存在一個JAR包里面
META-INF/services目錄涉及一個模式:服務提供者模式
引入該模式一般是為了程序松散耦合,而且是IoC(控制反轉(zhuǎn))的另一種實現(xiàn)方式
服務提供者模式一般有兩個角色
² 服務 :一般為一個抽象類
² 服務提供者 :抽象類的實現(xiàn)
存在META-INF/services目錄的JAR包一般為一個“服務提供者”
用NetBeans開發(fā)中小型程序不需要服務提供者概念,如果你要深入理解
可以Baidu或Google一下META-INF/services
l foo包
foo包是我們創(chuàng)建程序的主類foo.FooApp時,NetBeans自動為我們生成的,
我們來看看NetBeans為我們生成了什么基礎框架類:
1、FooApp.java

public class FooApp extends SingleFrameApplication
{



/** *//**

* 在啟動的時候創(chuàng)建和顯示程序的主框架

*/


@Override protected void startup()
{

show(new FooView(this));

}


/** *//**

*這個方法是用相應的資源注入到特定的Window來初始化程序

*因為我們用NetBeansk開發(fā),所以相應的資源是通過Gui生成器來完成

*也就是不用我們手動編寫代碼

*所以這個方法不是必須的

*/


@Override protected void configureWindow(java.awt.Window root)
{

}


/** *//**

* 一個方便的靜態(tài)獲取器,用來獲取FooApp類的實例

*/


public static FooApp getApplication()
{

return Application.getInstance(FooApp.class);

}


/** *//**

* 程序的運行入口點

*/


public static void main(String[] args)
{

launch(FooApp.class, args);

}

}

FooApp.java里面的代碼是NetBean為我們生成的,開發(fā)簡單的程序這里基本上不需要更改什么。
2、FooAboutBox.java
每個程序都應該有一個關于界面,Netbeans為我們生成了關于的模板
修改相應文字就可以了,當然也可以自己做個About界面

3、FooView.java
FooView.java是程序View部分的關鍵,由于生成的代碼比較多
在解釋代碼之前,我先看看Design部分

NetBenas給我們生成的框架非常簡單明了
需要注意的是圖上標識的兩個地方
標有1的地區(qū)為消息地區(qū),我們的程序完成任務后,在這里提示消息
標有2的地區(qū)為任務進度條,程序執(zhí)行任務時在這里現(xiàn)實任務進度,當然任務有確定模式和不確定模式,不確定模式的進度條持續(xù)地顯示動畫來表示正進行的操作。
接下來我們看看FooView.java的構(gòu)造器的代碼:

public FooView(SingleFrameApplication app)
{

super(app);

initComponents();

ResourceMap resourceMap = getResourceMap();

int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");


messageTimer = new Timer(messageTimeout, new ActionListener()
{


public void actionPerformed(ActionEvent e)
{

statusMessageLabel.setText("");

}

});

messageTimer.setRepeats(false);

int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");


for (int i = 0; i < busyIcons.length; i++)
{

busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");

}


busyIconTimer = new Timer(busyAnimationRate, new ActionListener()
{


public void actionPerformed(ActionEvent e)
{

busyIconIndex = (busyIconIndex + 1) % busyIcons.length;

statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);

}

});

idleIcon = resourceMap.getIcon("StatusBar.idleIcon");

statusAnimationLabel.setIcon(idleIcon);

progressBar.setVisible(false);

// connecting action tasks to status bar via TaskMonitor

TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());

taskMonitor.addPropertyChangeListener(


new java.beans.PropertyChangeListener()
{


public void propertyChange(java.beans.PropertyChangeEvent evt)
{

String propertyName = evt.getPropertyName();


if ("started".equals(propertyName))
{


if (!busyIconTimer.isRunning())
{

statusAnimationLabel.setIcon(busyIcons[0]);

busyIconIndex = 0;

busyIconTimer.start();

}

progressBar.setVisible(true);

progressBar.setIndeterminate(true);


} else if ("done".equals(propertyName))
{

busyIconTimer.stop();

statusAnimationLabel.setIcon(idleIcon);

progressBar.setVisible(false);

progressBar.setValue(0);


} else if ("message".equals(propertyName))
{

String text = (String)(evt.getNewValue());

statusMessageLabel.setText((text == null) ? "" : text);

messageTimer.restart();


} else if ("progress".equals(propertyName))
{

int value = (Integer)(evt.getNewValue());

progressBar.setVisible(true);

progressBar.setIndeterminate(false);

progressBar.setValue(value);

}

}

});

}

一大段代碼,看起來是不是眼花繚亂啊,其實這么一大斷代碼是IDE給我們生成的用來初始發(fā)剛才所示的兩個區(qū)域的消息區(qū)和進度條區(qū)域的。
也就是初始化消息區(qū)域和進度條區(qū)域,如果不想自定義消息現(xiàn)實和任務進度條的實現(xiàn)方式,上面一大段代碼大可不必理會。
我們再來看一段代碼:
@Action


public void showAboutBox()
{


if (aboutBox == null)
{

JFrame mainFrame = FooApp.getApplication().getMainFrame();

aboutBox = new FooAboutBox(mainFrame);

aboutBox.setLocationRelativeTo(mainFrame);

}

FooApp.getApplication().show(aboutBox);

}

看到方法名我們就知道,這個是用來實現(xiàn)關于界面的
我們運行一下程序,在點擊菜單欄里面的 Help > About..

結(jié)果,關于界面跳了出來。
看到這里你也許會驚訝,因為按照傳統(tǒng)的方法,相應的菜單應該有相應的ActionListener事情監(jiān)聽器才能發(fā)生事件啊!
別著急,你看showAboutBox()方法前不是有一個注解: @Action
這個注解涉及到SAF對動作的定義,我們后面將詳細講解
l foo.resources、foo.resources.busyicons包
foo.resources、foo.resources.busyicons包是用來存放程序的圖像、文字等資源的地方
后面講到程序的國際化的時候再作介紹。
忘掉ActionListener事件監(jiān)聽器,擁抱@Action
上面我們已經(jīng)提到,沒有相應的ActionListener事件監(jiān)聽器,點擊菜單欄里面的 Help > About..也照樣能觸發(fā)事件,這是怎么回事?
原來是SAF框架對有@Action注釋的方法進行了管理。
既然用原來的ActionListener已經(jīng)處理得非常好,為什么SAF專家組的人還弄個@Action,這不是沒事找事干嗎?呵呵,下面我們通過幾個例子來說明引入@Action的好處
l 制作環(huán)境相關的按鈕

我們經(jīng)常遇到這樣的按鈕或菜單,一般情況下它是不可用的,如上圖畫圈的三個按鈕,要等一定的條件他們自動啟用。
我舉例用@Action做這樣的按鈕看看,不用擔心,不必寫多少代碼,IDE幫我們完成了大部分的工作
我們要做的功能如下
點擊選擇CheckBox選項后,按鈕自動開啟,取消選中后按鈕變灰色,不可用。

在右邊的Palette屬性面板中拖出我們需要的兩個組件到程序主界面,并修改Text文字

右鍵點擊按鈕,選擇 Set Action…

在跳出來對話框中,在Action 選擇框中選中Create New Action

接下填寫按鈕的相關信息,這里我做一下簡單的說明
Action’s Class :動作方法存放在那個類中
Action’s Method:動作方法的名稱,比如上面提到的跳出關于對話框的方法showAboutBox()
Background Task選項:這個暫時不理它,后面將講到
Attributes :1、Basic標簽 設置按鈕的顯示文字、快捷鍵、按鈕圖像。
2、Advanced標簽可要注意了,因為這個和我們現(xiàn)在要做的例子關系非常大,我們在Advanced標簽里的Enabled Preproty 寫上 hasSelect(名字可以自己寫),如下面第二個圖


單擊OK,馬上看到IDE為我們生成的代碼

這樣我們就可以通過setHasSelect()方法來設置按鈕的可用性,setHasSelect(ture) 時按鈕開啟,setHasSelect(false)時按鈕不可用。
接下來選擇CheckBox組件,單擊右鍵,再選擇下圖所示

出來代碼中寫上一行代碼:
setHasSelect( ((JCheckBox)evt.getSource()).isSelected());
最后是這樣子的

private void jCheckBox1ItemStateChanged(java.awt.event.ItemEvent evt)
{

setHasSelect( ((JCheckBox)evt.getSource()).isSelected());

}

自此環(huán)境相關的按鈕就做完了,我們可以運行代碼試試。
從上面的例子中我們看到,做這個例子真正只寫了一行代碼:
setHasSelect( ((JCheckBox)evt.getSource()).isSelected());
其它都是通過IDE生成的,多虧IDE和SAF的Action我們才能如此高效的制作程序。
l 菜單和按鈕關聯(lián)
菜單和按鈕經(jīng)常表現(xiàn)為同一個行為,我們當然可以各自實現(xiàn)它,但是這樣就產(chǎn)生了冗余
編寫程序最總要的一點就是不要重復代碼。在Design模式下右鍵單擊相應的菜單,在Set Action…操作里面選擇相應Action
這樣菜單和按鈕就關聯(lián)起來了,該菜單和按鈕行為相同,SAF如此簡單的為我們完成了任務。
這又是Action解決了一大問題
l Background Task
剛才在制作環(huán)境相關的按鈕時,我們忽略一個選項

現(xiàn)在我們來介紹這個非常重要的特性
以前人們不斷抱怨Swing是如何慢,其實JRE1.5后,特別是1.6后Swing性能已經(jīng)完全不是問題,問題是開發(fā)Swing的程序員沒有處理好Swing的線程問題,比如本該在后臺運行的任務卻直接寫在Swing中,造成Swing程序相應緩慢。
在以前你要非常熟練Swing和小心才能寫出高質(zhì)量的Swing程序來,但是現(xiàn)在你利用SAF提供的框架支持,處理后臺任務變得非常容易。
我們 Set Action 的時候把Background Task選項選中,最后看一下IDE給我們生成了什么代碼:
原來的代碼:
@Action(enabledProperty = "hasSelect")


public Task actionMethodName()
{

}

Background Task選項選中后為如下代碼:
@Action(enabledProperty = "hasSelect")


public Task actionMethodName()
{

return new ActionMethodNameTask(getApplication());

}

private class ActionMethodNameTask extends


org.jdesktop.application.Task<Object, Void>
{


ActionMethodNameTask(org.jdesktop.application.Application app)
{

// Runs on the EDT. Copy GUI state that

// doInBackground() depends on from parameters

// to ActionMethodNameTask fields, here.

super(app);

}


@Override protected Object doInBackground()
{

// Your Task's code here. This method runs

// on a background thread, so don't reference

// the Swing GUI from here.

return null; // return your result

}


@Override protected void succeeded(Object result)
{

// Runs on the EDT. Update the GUI based on

// the result computed by doInBackground().

}

}

可以看到,actionMethodName()方法里運行的代碼將在一個新線程中運行
如果你要徹底明白上面的代碼和EDT是什么概念,請瀏覽下面的博客地址
http://blog.sina.com.cn/swingjava中有關Swing部分。
如果您懶得瀏覽,那么請遵循下面的建議
1、在ActionMethodNameTask構(gòu)造器中
代碼運行在EDT(事件分派進程)中,在這里復制doInBackground() 方法中所需要的GUI 的狀態(tài)或參數(shù)到ActionMethodNameTask的成員變量中。
2、doInBackground()方法中,不要存放處理GUI界面的代碼,該方法返回的值將自動傳到后面succeeded(Object Result)中
3、succeeded()運行在EDT中,任務成功的執(zhí)行后才執(zhí)行該方法,該方法主要收集doInBackground()方法運行后的結(jié)果以用來更新GUI程序。
我們做一下試驗,為ActionMethodNameTask增加一些代碼:
private class ActionMethodNameTask extends


org.jdesktop.application.Task<Object, Void>
{


ActionMethodNameTask(org.jdesktop.application.Application app)
{

super(app);

}


@Override protected Object doInBackground()
{


for(int i=0;i<1000000;i++)
{

System.out.println(i);

}

return null;

}


@Override protected void succeeded(Object result)
{

setMessage("運行成功");

}

}

運行程序后點擊按鈕
任務開始:

任務結(jié)束:

注意提示信息只顯示一定時間,這個時間可以自己設定,一定時間后信息自動消失。
l 制作阻塞按鈕

我經(jīng)常要做具有阻塞功能的按鈕,比如我們按一下按鈕,就跳出如上對話框,程序在后臺運行任務,但是我們不能進行其它操作,除非我們點擊取消按鈕。
制作這樣的按鈕,其實非常簡單:
在Set Action...進行如下設置就可以了

Blocking Type 可以選擇NONE,ACTION,COMPONENT,WINDOW,APPLICATION指定阻塞的范圍空間,你可以自己嘗試一下,效果是怎樣的。
從上面幾個小例子可以看到引入@Action巨大好處了吧。
國際化支持
正如開始所說的,SAF本身就很好的支持了國際化,再加上NetBeans輔助,國際化簡直易如反掌。
我們知道一些企業(yè)框架比如Structs、JSF是通過后綴名為properties的文件保存相關資源的。
SAF也是,我們看看IDE為我們生成的properties文件

從上圖可以看出,
FooApp.java
FooAboutBox.java
FooView.java
這三個類在foo.resources包分別對應有后綴名為properties的文件
FooApp. properties
FooAboutBox. properties
FooView. Properties
也就是說,類里面用到文字資源,圖像資源集中放到了一起。這樣國際化就相當簡單了
這時也許你會說,一開始就寫properties的文件,豈不是很麻煩?
別忘了,我們NetBeans的支持,做這個工作幾乎不費你任何力氣

在可視化編輯頁面,我直接修改相應文字,IDE會自動為我們把相應的文字保存到對應的properties的文件中。
根本不用自己手動敲寫下面的文字
jCheckBox1.text=選中按鈕開啟
尾聲
關于用NetBeans開發(fā)Swing Application Framework程序其實還有很多我沒有說清楚,有些也很難說清楚,關鍵還是要自己親身試驗一下,正如大牛們所說的“理論看不懂就去實踐,實踐不懂就去看理論”。
本文僅起到入門介紹的作用。開發(fā)需要的SAF JavaDoc文檔可以下面所示查看:

為了你更好的理解,我用SAF做一個端口掃描程序,這個程序很不完善,僅做演示:

點擊這里下載源代碼:
PortScan.rar
(提示:用NetBeans6.X打開項目目錄即可)
用SAF做的更完整的程序:
l mp3在線搜索工具

你可以到作者博客查看更詳細信息:
http://www.tkk7.com/huliqing/archive/2008/03/26/188817.html
l 單詞Mp3隨身寶DIY

本人的英語成績一直不是很好,特別是詞匯量很低,很多單詞不會發(fā)音。
所以我就有個想法,能不能把自己想要背記的單詞做成MP3的形式,單詞和單詞的解釋放到對應.lrc文件中,隨意挑選單詞,
制作成帶同步歌詞(LRC文件)的MP3文件,這樣在MP3機上就能邊聽單詞邊同步看單詞及其解釋,單詞先后次序、間隔多長,
可以相應自由地調(diào)整,讓我們的MP3機變成英語學習機!我通過網(wǎng)絡尋找這樣的軟件。
但是找了好久就是找不到合適的軟件,只是有一個《我愛背單詞》的軟件有類似這樣的一個功能,但是該軟件是閉源收費軟件,
免費版的語音是合成的,效果非常差。求人不如求自己,于是產(chǎn)生制作這個程序的想法。
這個程序原本就是想開源的,但是我拿去參加學校的電腦作品賽了,比賽的作品先前不能發(fā)布。所以暫時不發(fā)布程序和源代碼,等比賽結(jié)束再上傳程序和源代碼。這個可能要一段時間,請大家耐心等待消息。
l 用NetBeans開發(fā)但是沒有用SAF的YOYOPlayerMp3播放器

詳細信息請看作者的博客:
http://www.tkk7.com/hadeslee/archive/2007/12/31/171678.html
通過上面幾個程序可以體現(xiàn)出NetBeans的強大特性來(不僅僅是做桌面程序哦)!SAF框架加上NetBeans,我們可以專注程序的功能而不是程序的基礎實施上,而且即使是初學者也可以編寫出高性能的Swing程序來。我相信隨著越來越多的人了解SAF和NetBeans,后面會不斷涌現(xiàn)出新的Swing桌面程序來,讓我們拭目以待吧!
由于水平有限,文中有些術語解釋可能不準確,歡迎批評指正!(這也是我寫博客的原因,通過他人認識自己的不足)
版權聲明:
本文由令狐蟲原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請保留博客地址:
http://www.tkk7.com/linghuchong/