用 @Action 標(biāo)注定義動(dòng)作
@Action標(biāo)注打算作為Action的actionPerformed方法。ApplicationContext.getActionMap方法建立了包含由某些類定義的每個(gè)@Action的Action對(duì)象的ActionMap。ActionMap將父類鏈接起來(lái),并且每個(gè)Application的子類都有父類。這樣,所有的應(yīng)用程序繼承了從Application基類定義的cut,copy,paste,delete和quit動(dòng)作。
SingleFrameExample4.java定義了兩個(gè)@Actions,open和close:
/**
* Load the specified file into the textPane or popup an error
* dialog if something goes wrong.
*/
@Action public void open() {
JFileChooser chooser = new JFileChooser();
int option = chooser.showOpenDialog(getMainFrame());
if (option == JFileChooser.APPROVE_OPTION) {
File file = chooser.getSelectedFile();
textPane.setPage(file.toURI().toURL());
// error handling omitted for clarity
}
}
/**
* Replace the contents of the textPane with the value of the
* "defaultText" resource.
*/
@Action public void close() {
ApplicationContext ac = ApplicationContext.getInstance();
String ac.getResourceMap(getClass()).getString("defaultText");
textPane.setText(defaultText);
}
使用ApplicationContext的getActionMap方法來(lái)建立包含open和close的ActionMap。為了簡(jiǎn)化查詢,通過(guò)建立一個(gè)私有的工具方法來(lái)完成,這和上面的查詢app類的ResourceMap相似。
private javax.swing.Action getAction(String actionName) {
ApplicationContext ac = ApplicationContext.getInstance();
return ac.getActionMap(getClass(), this).get(actionName);
}
/* We use getAction to set the action property of menu items
* and buttons and so on:
*/
openMenuItem.setAction(getAction("open"));
closeMenuItem.setAction(getAction("close"));
以這種方法定義的Action的缺省表示特性當(dāng)從類的ResourceBundle載入時(shí)初始化。這樣,Action的text, mnemonic, tooltip (shortDescription), and shortcut被定義在SingleFrameExample4.properties的ResourceBundle里:
open.Action.text = &Open...
open.Action.accelerator = control O
open.Action.shortDescription = open a document
close.Action.text = &Close
close.Action.shortDescription = close the document
如果運(yùn)行,將看見(jiàn)Action及其所有資源。
SingleFrameExample4 屏幕截圖
編寫動(dòng)作可能困難的一個(gè)方面是處理那些潛在占用漫長(zhǎng)時(shí)間或者阻塞的任務(wù),比如文件系統(tǒng)或者網(wǎng)絡(luò)訪問(wèn)。應(yīng)用程序必須在后臺(tái)線程里完成這些任務(wù),讓Swing事件分派線程(EDT)不會(huì)阻塞。在本例中,由JTextPane類處理異步文件載入。在許多情況下,應(yīng)用程序開(kāi)發(fā)者必須直接處理在后臺(tái)上的運(yùn)行任務(wù)。應(yīng)用程序框架Task類(基于SwingWorker)簡(jiǎn)化了異步執(zhí)行的動(dòng)作的編寫。
產(chǎn)生后臺(tái)任務(wù)的動(dòng)作
SwingWorker類在后臺(tái)線程上計(jì)算數(shù)值,然后在事件分派線程上調(diào)用完成方法。應(yīng)用程序通過(guò)覆蓋SwingWorker done方法,或者增加一個(gè)PropertyChangeListener,或者覆蓋process方法,能夠監(jiān)視SwingWorker并安全刷新GUI。它們當(dāng)后臺(tái)線程調(diào)用publish()方法時(shí)收到中間結(jié)果。能夠測(cè)量它們自己進(jìn)度的SwingWorker,設(shè)置進(jìn)度特性通知PropertyChangeListener已經(jīng)完成工作的百分比。
應(yīng)用程序框架定義一個(gè)叫做Task的SwingWorker的子類,Task加入了一些特性讓后臺(tái)線程比較容易監(jiān)視和管理。任務(wù)自動(dòng)初始化從ResourceBundle載入的描述性特性。@Action方法能夠返回一個(gè)Task對(duì)象,而框架將執(zhí)行后臺(tái)線程的Task。例如,這里的Task只是睡眠大約1500毫秒。
class DoNothingTask extends Task {
@Override protected Void doInBackground() throws InterruptedException {
for(int i = 0; i < 10; i++) {
setMessage("Working
[" + i + "]");
Thread.sleep(150L);
setProgress(i, 0, 9);
}
Thread.sleep(150L);
return null;
}
@Override protected void done() {
setMessage(isCancelled() ? "Canceled." : "Done.");
}
}
盡管DoNothingTask產(chǎn)生一個(gè)消息并周期地更新進(jìn)度特性,但是大多數(shù)情況下它只是睡眠。顯而易見(jiàn),這類事情不必在事件分派線程上執(zhí)行。啟動(dòng)在后臺(tái)線程上DoNothingTask的@Action可能像這樣編寫:
@Action Task JustDoNothing() {
return new DoNothingTask();
}
ActionExample4.java提供了一個(gè)孵化Task的@Action的有趣示例。它使用一個(gè)遍歷枚舉目錄里所有文件的Task(ListFileTask),每次發(fā)布大約10個(gè)文件。Task使用publish方法將中間結(jié)果交付給運(yùn)行在EDT上的process方法。ActionExample4.java通過(guò)建立一個(gè)覆蓋process方法的應(yīng)用程序的子類使用ListFilesTask來(lái)更新GUI:
private class DoListFiles extends ListFilesTask {
public DoListFiles(File root) { // ctor runs on the EDT
super(root);
listModel.clear();
}
@Override protected void process(List files) {
if (!isCancelled()) {
listModel.addAll(files);
}
}
}
private Task doListFiles = null;
@Action
public Task go() {
stop(); // stop the pending DoListFiles task (if any)
setStopEnabled(true);
File root = new File(rootTextField.getText());
return new DoListFiles(root);
}
@Action(enabledProperty = "stopEnabled")
public void stop() {
if ((doListFiles != null) && !doListFiles.isCancelled()) {
if (doListFiles.cancel(true)) {
doListFiles = null;
setStopEnabled(false);
}
}
}
如果運(yùn)行ActionExample4,將會(huì)注意到當(dāng)應(yīng)用程序后臺(tái)正忙于枚舉文件時(shí),GUI依然保持可響應(yīng)狀態(tài)。本例以PropertyChangeListener監(jiān)視由后臺(tái)Task產(chǎn)生的消息并在窗口底部將它們顯示出來(lái)(這段代碼在上面未列出)。監(jiān)視后臺(tái)任務(wù)的狀態(tài)典型地由TaskMonitor類處理。SingleFrameExample5使用TaskMonitor類,它是下節(jié)的主題。
ActionExample4 屏幕截圖
本例也引入了綁定啟用@Action狀態(tài)為特性當(dāng)前特性值的enabledProperty的標(biāo)注參數(shù)。也存在一個(gè)指示GUI在后臺(tái)任務(wù)運(yùn)行時(shí)應(yīng)當(dāng)阻塞的block標(biāo)注參數(shù)(演示未體現(xiàn))。對(duì)示例中@Action(block = Block.ACTION)意味著當(dāng)后臺(tái)任務(wù)正運(yùn)行時(shí)Action對(duì)象自身應(yīng)當(dāng)禁止。為了用模態(tài)對(duì)話框阻塞全部GUI,通過(guò)指定block = lock.APPLICATION來(lái)替代。ActionExample5.java演示了所有的可能性。
一個(gè)小而全的應(yīng)用程序
SingleFrameExample5.java是迄今為止提供的最接近完整的應(yīng)用程序。通過(guò)從JPL photojournal網(wǎng)址上下載某些非常巨大的火星探測(cè)器的圖像,它打算突出后臺(tái)任務(wù)的重要性。應(yīng)用程序允許用戶一步一步下載圖像,并且搶先或者取消當(dāng)前的下載/顯示任務(wù)。應(yīng)用程序的結(jié)構(gòu)是典型的,包括了用將通用任務(wù)和應(yīng)用程序GUI連接的子類(ShowImageTask)特化通用Task類(本例中的LoadImageTask)。
在前一節(jié)描述了Task管理大多數(shù)基礎(chǔ)。幾個(gè)額外的細(xì)節(jié)值得在此強(qiáng)調(diào):
- StatusBar使用共享的TaskMonitor來(lái)顯示當(dāng)前"前臺(tái)”任務(wù)。
- 通過(guò)顯式終止圖像讀取操作(如果操作正在進(jìn)行),LoadImageTask需要特別處理取消任務(wù)。通過(guò)覆蓋Task.done(不是cancel,它是最終結(jié)構(gòu))方法來(lái)處理。
- 正如@Action所為,通過(guò)缺省的TaskService執(zhí)行一個(gè)Task的ready()方法,顯示第一幅圖像。
SingleFrameExample5屏幕截圖: Mars Rover(火星探測(cè)器)的著陸視圖
(第一部分,請(qǐng)參見(jiàn):Swing應(yīng)用程序框架(Swing Application Framework)API緒論(JSR-296)之一(翻譯))