1. 程式人生 > >Swing應用程式框架(Swing Application Framework)API緒論(JSR-296)之二(翻譯)

Swing應用程式框架(Swing Application Framework)API緒論(JSR-296)之二(翻譯)

 

用 @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 
publicvoid 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 
publicvoid close() {
    ApplicationContext ac 
= ApplicationContext.getInstance();
    String ac.getResourceMap(getClass()).getString(
"defaultText");
    textPane.setText(defaultText);
}
 

使用ApplicationContext的getActionMap方法來建立包含open和close的ActionMap。為了簡化查詢,通過建立一個私有的工具方法來完成,這和上面的查詢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的預設表示特性當從類的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 document

close.Action.text = &Close
close.Action.shortDescription = close the document

如果執行,將看見Action及其所有資源。

SingleFrameExample4-screenshot.png

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毫秒。

class DoNothingTask extends Task {
    @Override 
protected Void doInBackground() throws InterruptedException {
    
for(int i =0; i <10; i++) {
        setMessage(
"Workingdot.gif ["+ i +"]");
        Thread.sleep(
150L);
        setProgress(i, 
09);
    }
    Thread.sleep(
150L);
    
returnnull;
    }
    @Override 
protectedvoid 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:

privateclass DoListFiles extends ListFilesTask {
    
public DoListFiles(File root) {  // ctor runs on the EDTsuper(root);
    listModel.clear();
    }
    @Override 
protectedvoid 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());
    
returnnew DoListFiles(root);
}

@Action(enabledProperty 
="stopEnabled")
publicvoid stop() {
    
if ((doListFiles !=null&&!doListFiles.isCancelled()) {
    
if (doListFiles.cancel(true)) {
        doListFiles 
=null;
        setStopEnabled(
false);
    }
    }
}
    

如果執行ActionExample4,將會注意到當應用程式後臺正忙於列舉檔案時,GUI依然保持可響應狀態。本例以 PropertyChangeListener監視由後臺Task產生的訊息並在視窗底部將它們顯示出來(這段程式碼在上面未列出)。監視後臺任務的狀態典 型地由TaskMonitor類處理。SingleFrameExample5使用TaskMonitor類,它是下節的主題。

ActionExample4-screenshot.png

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()方法,顯示第一幅影象。

SingleFrameExample5-screenshot.png

SingleFrameExample5螢幕截圖: Mars Rover(火星探測器)的著陸檢視