用 @Action 標註定義動作
@Action標註打算作爲Action的actionPerformed方法。ApplicationContext.getActionMap 方法建立了包含由某些類定義的每個@Action的Action對象的ActionMap。ActionMap將父類鏈接起來,並且每個 Application的子類都有父類。這樣,所有的應用程序繼承了從Application基類定義的cut,copy,paste,delete和 quit動作。
SingleFrameExample4.java定義了兩個@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方法來建立包含open和close的ActionMap。爲了簡化查詢,通過建立一個私有的工具方法來完成,這和上面的查詢app類的ResourceMap相似。
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的缺省表示特性當從類的ResourceBundle載入時初始化。這樣,Action的text, mnemonic, tooltip (shortDescription), and shortcut被定義在SingleFrameExample4.properties的ResourceBundle裏:
open.Action.text = &Open...
open.Action.accelerator = control O
open.Action.shortDescription = open a documentclose.Action.text = &Close
close.Action.shortDescription = close the document
如果運行,將看見Action及其所有資源。
SingleFrameExample4 屏幕截圖
編寫動作可能困難的一個方面是處理那些潛在佔用漫長時間或者阻塞的任務,比如文件系統或者網絡訪問。應用程序必須在後臺線程裏完成這些任務,讓 Swing事件分派線程(EDT)不會阻塞。在本例中,由JTextPane類處理異步文件載入。在許多情況下,應用程序開發者必須直接處理在後臺上的運 行任務。應用程序框架Task類(基於SwingWorker)簡化了異步執行的動作的編寫。
產生後臺任務的動作
SwingWorker類在後臺線程上計算數值,然後在事件分派線程上調用完成方法。應用程序通過覆蓋SwingWorker done方法,或者增加一個PropertyChangeListener,或者覆蓋process方法,能夠監視SwingWorker並安全刷新 GUI。它們當後臺線程調用publish()方法時收到中間結果。能夠測量它們自己進度的SwingWorker,設置進度特性通知 PropertyChangeListener已經完成工作的百分比。
應用程序框架定義一個叫做Task的SwingWorker的子類,Task加入了一些特性讓後臺線程比較容易監視和管理。任務自動初始化從 ResourceBundle載入的描述性特性。@Action方法能夠返回一個Task對象,而框架將執行後臺線程的Task。例如,這裏的Task只 是睡眠大約1500毫秒。
@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產生一個消息並週期地更新進度特性,但是大多數情況下它只是睡眠。顯而易見,這類事情不必在事件分派線程上執行。啓動在後臺線程上DoNothingTask的@Action可能像這樣編寫:
@Action Task JustDoNothing() {
return new DoNothingTask();
}
ActionExample4.java提供了一個孵化Task的@Action的有趣示例。它使用一個遍歷枚舉目錄裏所有文件的 Task(ListFileTask),每次發佈大約10個文件。Task使用publish方法將中間結果交付給運行在EDT上的process方法。 ActionExample4.java通過建立一個覆蓋process方法的應用程序的子類使用ListFilesTask來更新GUI:
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);
}
}
}
如果運行ActionExample4,將會注意到當應用程序後臺正忙於枚舉文件時,GUI依然保持可響應狀態。本例以 PropertyChangeListener監視由後臺Task產生的消息並在窗口底部將它們顯示出來(這段代碼在上面未列出)。監視後臺任務的狀態典 型地由TaskMonitor類處理。SingleFrameExample5使用TaskMonitor類,它是下節的主題。
ActionExample4 屏幕截圖
本例也引入了綁定啓用@Action狀態爲特性當前特性值的enabledProperty的標註參數。也存在一個指示GUI在後臺任務運 行時應當阻塞的block標註參數(演示未體現)。對示例中@Action(block = Block.ACTION)意味着當後臺任務正運行時Action對象自身應當禁止。爲了用模態對話框阻塞全部GUI,通過指定block = lock.APPLICATION來替代。ActionExample5.java演示了所有的可能性。
一個小而全的應用程序
SingleFrameExample5.java是迄今爲止提供的最接近完整的應用程序。通過從JPL photojournal網址上下載某些非常巨大的火星探測器的圖像,它打算突出後臺任務的重要性。應用程序允許用戶一步一步下載圖像,並且搶先或者取消 當前的下載/顯示任務。應用程序的結構是典型的,包括了用將通用任務和應用程序GUI連接的子類(ShowImageTask)特化通用Task類(本例 中的LoadImageTask)。
在前一節描述了Task管理大多數基礎。幾個額外的細節值得在此強調:
- StatusBar使用共享的TaskMonitor來顯示當前"前臺”任務。
- 通過顯式終止圖像讀取操作(如果操作正在進行),LoadImageTask需要特別處理取消任務。通過覆蓋Task.done(不是cancel,它是最終結構)方法來處理。
- 正如@Action所爲,通過缺省的TaskService執行一個Task的ready()方法,顯示第一幅圖像。