在Windows操作系統中,最常用的進度條對話框就是文件復制時的彈出框,如果想讓用戶愉快的使用你開發
的軟件,那么在執行某個較長時間的操作時候,就應該彈出一個進度條提示框,告訴用戶程序正在做什么.
做到什么地步了.進度條提示框可以讓用戶更有安全感也可以提高用戶的耐心.
前面用到的SWT組件的進度條ProgressBar類太底層,還需要處理諸如線程分配的問題.現在
JFace提供了更容易使用的ProgressMonitorDialog類來創建進度條對話框.
標准的進度條對話框:
ProgressMonitorDialog類還算簡單,只要將執行代碼用IRunnableWithProgress類包裝一下即可,本實例中在窗口中建立 一個GO按鈕,當單擊該按鈕時執行一個需時較長的操作(以每次循環停頓一秒來模擬),並彈出一個進度條對話框.程序在未加進度條時的代碼如下:
ProgressMonitorDialog.java
1 import org.eclipse.core.runtime.IProgressMonitor; 2 import org.eclipse.jface.dialogs.ProgressMonitorDialog; 3 import org.eclipse.jface.operation.IRunnableWithProgress; 4 import org.eclipse.swt.SWT; 5 import org.eclipse.swt.events.SelectionAdapter; 6 import org.eclipse.swt.events.SelectionEvent; 7 import org.eclipse.swt.layout.RowLayout; 8 import org.eclipse.swt.widgets.Button; 9 import org.eclipse.swt.widgets.Display; 10 import org.eclipse.swt.widgets.Shell; 11 12 public class ProgressMonitorDialog1 { 13 14 public static void main(String[] args) { 15 new ProgressMonitorDialog1().open(); 16 } 17 18 public void open() { 19 final Display display = Display.getDefault(); 20 final Shell shell = new Shell(); 21 shell.setSize(500, 375); 22 23 shell.setLayout(new RowLayout()); 24 Button button = new Button(shell, SWT.NONE); 25 button.setText(" GO "); 26 button.addSelectionListener(new SelectionAdapter() { 27 public void widgetSelected(SelectionEvent e) { 28 // 創建進度條對話框的處理過程對象 29 IRunnableWithProgress runnable = new IRunnableWithProgress() { 30 public void run(IProgressMonitor monitor) { 31 monitor.beginTask("開始執行......", 10); 32 for (int i = 0; i < 10; i++) {// 循環10次,每次間隔一秒 33 if (monitor.isCanceled()) // 隨時監控是否選擇了對話框的“取消”按鈕 34 return;// 中斷處理 35 try { 36 Thread.sleep(1000); 37 } catch (Throwable t) {} 38 monitor.setTaskName("第" + (i + 1) + "次循環");// 提示信息 39 monitor.worked(1);// 進度條前進一步 40 } 41 monitor.done();// 進度條前進到完成 42 } 43 }; 44 try { 45 // 創建一個進度條對話框,並將runnable傳入 46 // 第一個參數推薦設為true,如果設為false則處理程序會運行在UI線程里,界面將有一點停滯感。 47 // 第二個參數:true=對話框的“取消”按鈕有效 48 new ProgressMonitorDialog(shell).run(true, true, runnable); 49 } catch (Exception e2) { 50 e2.printStackTrace(); 51 } 52 } 53 }); 54 55 shell.layout(); 56 shell.open(); 57 while (!shell.isDisposed()) { 58 if (!display.readAndDispatch()) 59 display.sleep(); 60 } 61 } 62 63 }
運行結果:

要在執行程序時候彈出一個進度條,則就要對GO按鈕的widgetSelected方法做修改(上面代碼已經修改完畢):
· 編寫一個處理過程接口IRunnableWithProgress的匿名實現類,並將耗時代碼嵌入其run方法中,可以用run方法的參數monitor操作進度條向前移動.當然這個類寫成命名內部類或者外部類也是可以的.
·創建一個ProgressMonitorDialog對話框對象,並用它的run方法彈出進度條對話框.接收IRunnableWithProgress對象做參數.run方法有受控異常需要處理,需要用try/catch括起來.
程序說明:如果無法確定進度條需要多少格長度,可以將值設置的大一些,即使真實循環數沒有達到或者超過了也不會引起程序出錯.
進度條對話框的實現代碼看是復雜,其實簡化而無變化,代碼框架都一個樣,關鍵是把IRunnableWithProgress.run方法中的處理程序寫好.
反復顯示的進度條對話框.
有些后台任務,可能無法將它分解成多少部,它就是一個單一的長時間的處理過程.這時可以彈出一個對話框,進度條不斷的重復移動,直到后台任務結束,或被中止.
實際上這種方案后台處理程序和前台彈出進度框被分為了兩個獨立的部分,但它們之間仍然需要一個信使來傳遞信息.比如,后台任務完成了,那么就應該通知前台進度框關閉:單擊了前台進度框的"取消"按鈕,也需要通知后台任務中斷處理.這個信使可以用一個boolean變量標志位來充當,不必去通知前台后台,自己監控該變量的變化即可.具體的實體如下代碼所示:
ProgressMonitorDialog2.java
1 import org.eclipse.core.runtime.IProgressMonitor; 2 import org.eclipse.jface.dialogs.ProgressMonitorDialog; 3 import org.eclipse.jface.operation.IRunnableWithProgress; 4 import org.eclipse.swt.SWT; 5 import org.eclipse.swt.events.SelectionAdapter; 6 import org.eclipse.swt.events.SelectionEvent; 7 import org.eclipse.swt.layout.RowLayout; 8 import org.eclipse.swt.widgets.Button; 9 import org.eclipse.swt.widgets.Display; 10 import org.eclipse.swt.widgets.Shell; 11 12 public class ProgressMonitorDialog2 { 13 14 public static void main(String[] args) { 15 new ProgressMonitorDialog2().open(); 16 } 17 18 public void open() { 19 final Display display = Display.getDefault(); 20 final Shell shell = new Shell(); 21 shell.setSize(500, 375); 22 23 shell.setLayout(new RowLayout()); 24 Button button = new Button(shell, SWT.NONE); 25 button.setText(" GO "); 26 button.addSelectionListener(new SelectionAdapter() { 27 private boolean stopFlag;// 停止標志 28 29 private void go() { 30 for (int i = 0; i < 10; i++) {// 循環10次,每次間隔一秒 31 System.out.println("第" + (i + 1) + "個任務執行完畢"); 32 if (stopFlag) {// 監控是否要讓停止后台任務 33 System.out.println("被中斷了"); 34 return; 35 } 36 try { 37 Thread.sleep(1000); 38 } catch (Throwable t) {} 39 } 40 stopFlag = true;// 執行完畢后把標志位設為停止,好通知給進度框 41 System.out.println("全部任務執行完畢"); 42 } 43 44 public void widgetSelected(SelectionEvent e) { 45 stopFlag = false;// 每次都設初值為false 46 new Thread() {// 把后台任務放到一個新開線程里執行 47 public void run() { 48 go(); 49 } 50 }.start(); 51 showProgressDialog();// 打開一個進度框 52 } 53 54 private void showProgressDialog() { 55 IRunnableWithProgress runnable = new IRunnableWithProgress() { 56 public void run(IProgressMonitor monitor) { 57 monitor.beginTask("正在執行中......", 30); 58 int i = 0; 59 while (true) { 60 // 監聽是否單擊了進度框的“取消”按鈕,stopFlag則是監聽后台任務是否停止 61 if (monitor.isCanceled() || stopFlag) { 62 stopFlag = true; // 單擊了“取消”按鈕要設標志位為停止,好通知后台任務中斷執行 63 break;// 中斷處理 64 } 65 // i到30后清零。並將進度條重新來過 66 if ((++i) == 30) { 67 i = 0; 68 monitor.beginTask("正在執行中......", 30); 69 } 70 // 進度條每前進一步體息一會,不用太長或太短,時間可任意設。 71 try { 72 Thread.sleep(99); 73 } catch (Throwable t) {} 74 monitor.worked(1);// 進度條前進一步 75 } 76 monitor.done();// 進度條前進到完成 77 } 78 }; 79 try { 80 new ProgressMonitorDialog(shell).run(true, true, runnable); 81 } catch (Exception e) { 82 e.printStackTrace(); 83 } 84 } 85 }); 86 87 shell.layout(); 88 shell.open(); 89 while (!shell.isDisposed()) { 90 if (!display.readAndDispatch()) 91 display.sleep(); 92 } 93 } 94 95 }
運行結果如下:

點擊cancle按鈕,對應的后面沒有執行的任務取消.

封裝反復顯示的進度條對話框
雖然實例2實現了一個不斷反復顯示的進度框,但它的代碼如此凌亂,而且許多代碼復生冗長,所以應該把這種不斷反復的進度框封裝成一個組件,讓其使用起來更方便.封裝成組件會遇到兩個問題:
(1)如何封裝?第一個想到的是集成ProgressMonitorDialog來實現新組件.這種方法未嘗不可,但這樣新組件就和ProgressMonitorDialog類耦合在了一起,如果以后不想用進度條而想改用一個動畫,就不容易了.所以還是用組合方式的擴展比較好.實現起來也簡單.
(2)封裝后分成了多個類.由於boolean是簡單類型是值傳遞.它已經不適合作為進度框和后台任務之間的信使.而Boolean類雖然是引用傳遞,但它是不可變化類.要改變它要重新創建一個對象.所以他也不能用.因此方案只有兩個,用boolean數組,或者創建一個專門的標識類做信使.這里選擇第二個方案.
打方案定了,接着開始做具體的實現,首先是這個新組件應該提供什么方法.可以從使用的角度來考慮,經過考慮之后使用方式可以參照ProgressMonitorDialog.具體代碼如下所示:
ProgressMonitorDialog3.java
1 import org.eclipse.swt.SWT; 2 import org.eclipse.swt.events.SelectionAdapter; 3 import org.eclipse.swt.events.SelectionEvent; 4 import org.eclipse.swt.layout.RowLayout; 5 import org.eclipse.swt.widgets.Button; 6 import org.eclipse.swt.widgets.Display; 7 import org.eclipse.swt.widgets.Shell; 8 9 10 public class ProgressMonitorDialog3 { 11 12 public static void main(String[] args) { 13 new ProgressMonitorDialog3().open(); 14 } 15 16 public void open() { 17 final Display display = Display.getDefault(); 18 final Shell shell = new Shell(); 19 shell.setSize(500, 375); 20 21 shell.setLayout(new RowLayout()); 22 Button button = new Button(shell, SWT.NONE); 23 button.setText(" GO "); 24 button.addSelectionListener(new SelectionAdapter() { 25 public void widgetSelected(SelectionEvent e) { 26 new LoopProgressDialog(shell).run(true, new IProgressDialogRunnable() { 27 public void run(BooleanFlag stopFlag) { 28 for (int i = 0; i < 10; i++) { 29 System.out.println("第" + (i + 1) + "個任務執行完畢"); 30 if (stopFlag.getFlag()) { 31 System.out.println("被中斷了"); 32 return; 33 } 34 try { 35 Thread.sleep(1000); 36 } catch (Throwable t) {} 37 } 38 stopFlag.setFlag(true); 39 System.out.println("全部任務執行完畢"); 40 } 41 }); 42 } 43 }); 44 45 shell.layout(); 46 shell.open(); 47 while (!shell.isDisposed()) { 48 if (!display.readAndDispatch()) 49 display.sleep(); 50 } 51 } 52 53 }
程序說明:LoopProgressDialog是這里的新組件.它的使用看起來非常像ProgressMonitorDialog,區別是:IProgressDialogRunnable是一個我們自己定義的接口,它提供一個run方法.方法的參數是一個用作信使的標志類BooleanFlag.BooleanFlag實際是對boolean的一個封裝,和Boolean封裝類不同的是它提供了setFlag修改方法.
IProgressDialogRunnable和BooleanFlag的代碼比較簡單.如下所示:
IProgressDialogRunnable.java
public interface IProgressDialogRunnable { void run(BooleanFlag stopFlag); }
BooleanFlag.java
1 public final class BooleanFlag { 2 private boolean flag = false; 3 4 public BooleanFlag() {} 5 6 public BooleanFlag(boolean flag) { 7 this.flag = flag; 8 } 9 10 public synchronized boolean getFlag() { 11 return flag; 12 } 13 14 public synchronized void setFlag(boolean flag) { 15 this.flag = flag; 16 } 17 }
接下來就是新組件LoopProgressDialog的實現了.
LoopProgressDialog.java
1 import org.eclipse.core.runtime.IProgressMonitor; 2 import org.eclipse.jface.dialogs.ProgressMonitorDialog; 3 import org.eclipse.jface.operation.IRunnableWithProgress; 4 import org.eclipse.swt.widgets.Shell; 5 6 7 public class LoopProgressDialog { 8 private ProgressMonitorDialog dialog; 9 public BooleanFlag stopFlag = new BooleanFlag(); 10 11 public LoopProgressDialog(Shell shell) { 12 dialog = new ProgressMonitorDialog(shell); 13 } 14 15 public void run(boolean cancelable, final IProgressDialogRunnable runnable) { 16 new Thread() { 17 public void run() { 18 runnable.run(stopFlag); 19 } 20 }.start(); 21 22 try { 23 dialog.run(true, cancelable, new LoopRunnable()); 24 } catch (Exception e) { 25 e.printStackTrace(); 26 } 27 } 28 29 private class LoopRunnable implements IRunnableWithProgress { 30 public void run(IProgressMonitor monitor) { 31 monitor.beginTask("正在執行中......", 30); 32 int i = 0; 33 while (true) { 34 // 監聽是否單擊了進度框的“取消”按鈕,stopFlag則是監聽后台任務是否停止 35 if (monitor.isCanceled() || stopFlag.getFlag()) { 36 stopFlag.setFlag(true); 37 break;// 中斷處理 38 } 39 // i到30后清零。並將進度條重新來過 40 if ((++i) == 30) { 41 i = 0; 42 monitor.beginTask("正在執行中......", 30); 43 } 44 // 進度條每前進一步體息一會,不用太長或太短,時間可任意設。 45 try { 46 Thread.sleep(99); 47 } catch (Throwable t) {} 48 monitor.worked(1);// 進度條前進一步 49 } 50 monitor.done();// 進度條前進到完成 51 } 52 } 53 54 }
用動畫GIF來表示進度
ProgressMonitorDialog4.java和ProgressMonitorDialog3.java唯一不同之處就是把LoopProgressDialog改成了動畫GIF界面類LoopImageDialog.
LoopImageDialog的內部實現沒有采用Dialog或者其子類,而是用Shell來彈出界面的.所以嚴格來說它不能算是一個對話框.
ProgressMonitorDialog4.java
1 import org.eclipse.swt.SWT; 2 import org.eclipse.swt.events.SelectionAdapter; 3 import org.eclipse.swt.events.SelectionEvent; 4 import org.eclipse.swt.layout.RowLayout; 5 import org.eclipse.swt.widgets.Button; 6 import org.eclipse.swt.widgets.Display; 7 import org.eclipse.swt.widgets.Shell; 8 9 10 public class ProgressMonitorDialog4 { 11 12 public static void main(String[] args) { 13 new ProgressMonitorDialog4().open(); 14 } 15 16 public void open() { 17 final Display display = Display.getDefault(); 18 final Shell shell = new Shell(); 19 shell.setSize(500, 375); 20 21 shell.setLayout(new RowLayout()); 22 Button button = new Button(shell, SWT.NONE); 23 button.setText(" GO "); 24 button.addSelectionListener(new SelectionAdapter() { 25 public void widgetSelected(SelectionEvent e) { 26 new LoopImageDialog(shell).run(true, new IProgressDialogRunnable() { 27 public void run(BooleanFlag stopFlag) { 28 for (int i = 0; i < 10; i++) { 29 System.out.println("第" + (i + 1) + "個任務執行完畢"); 30 if (stopFlag.getFlag()) { 31 System.out.println("被中斷了"); 32 return; 33 } 34 try { 35 Thread.sleep(1000); 36 } catch (Throwable t) {} 37 } 38 stopFlag.setFlag(true); 39 System.out.println("全部任務執行完畢"); 40 } 41 }); 42 } 43 }); 44 45 shell.layout(); 46 shell.open(); 47 while (!shell.isDisposed()) { 48 if (!display.readAndDispatch()) 49 display.sleep(); 50 } 51 } 52 53 }
LoopImageDialog.java
1 import org.eclipse.swt.SWT; 2 import org.eclipse.swt.graphics.ImageLoader; 3 import org.eclipse.swt.layout.FillLayout; 4 import org.eclipse.swt.widgets.Display; 5 import org.eclipse.swt.widgets.Shell; 6 7 import cn.com.chengang.swt.SWTUtils; 8 9 public class LoopImageDialog { 10 private Shell parentShell; 11 public BooleanFlag stopFlag = new BooleanFlag(); 12 13 protected LoopImageDialog(Shell parentShell) { 14 this.parentShell = parentShell; 15 } 16 17 public void run(boolean cancelable, final IProgressDialogRunnable runnable) { 18 new Thread() { 19 public void run() { 20 runnable.run(stopFlag); 21 } 22 }.start(); 23 showDialog(); 24 } 25 26 private void showDialog() { 27 final Display display = Display.getDefault(); 28 Shell shell = new Shell(parentShell); 29 shell.setSize(250, 180); 30 SWTUtils.setCenter(shell); // 使用shell居中 31 shell.setLayout(new FillLayout()); 32 // 在Shell窗口顯示一個動畫GIF 33 final ImageLoader imageLoader = new ImageLoader(); 34 imageLoader.load("icons/animation2.gif"); 35 new AnimationGIF(shell, SWT.BORDER, imageLoader); 36 // 彈出窗口。Shell和Dialog不同,它在open彈出界面之后會繼承執行下面的語句 37 shell.open(); 38 while (!shell.isDisposed()) { 39 if (stopFlag.getFlag()) 40 shell.dispose(); 41 if (!display.readAndDispatch()) 42 display.sleep(); 43 } 44 stopFlag.setFlag(true); 45 } 46 }
