是不是覺得Windows的倒計時關機對話框很酷?

其實你也可以通過SWT的對話框來實現這個功能,如果你真的夠無聊的話,可以用來嚇唬一下你的用戶,當然你的確定你的人緣很好才行.
好了,開場白到此為止,下面進入正題:
我設計這個倒計時對話框主要是為了讓我們系統在自動更新完成后自動重啟,我們的系統提供了自動更新的功能,而且有個數據傳輸模塊,數據傳輸模塊是使用Wrapper這個工具封裝Java程序成服務,數據傳輸模塊會共享RCP客戶端的插件Jar包,由于Eclipse插件的特殊性,自動更新完成后可能會在安裝目錄中存在同名稱但不同版本號的插件,具體細節省去7443個字節。
想實現倒計時對話框,我們可以使用Timer這個定時工具類,這主要考慮到倒計時對話框要和RCP項目的主線程區分開來,對話框彈出不能夠影響用戶的操作,至少得讓他們的有時間保存信息不至于丟失,我們把這個類命名為 TimerMessageDialog .java, 根據MessageDialog中提供的方法,我們無法動態顯示提示信息,比如還剩下多少時間.你可以重載MessageDialog的 createMessageArea(Composite composite) 方法,可以再TimerMessageDialog中新增一個字段引用 messageLabel ,比如 localMessageLabel ,然后對 localMessageLabel 的值進行修改,下面是我的實現,可以參考一下:
protected Control createMessageArea(Composite composite) {
// create composite
// create image
Image image = getImage();
if (image != null) {
imageLabel = new Label(composite, SWT.NULL);
image.setBackground(imageLabel.getBackground());
imageLabel.setImage(image);
// addAccessibleListeners(imageLabel, image);
GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.BEGINNING)
.applyTo(imageLabel);
}
// create message
if (message != null) {
messageLabel = new Label(composite, getMessageLabelStyle());
TimerMessageDialog.localMessageLabel = messageLabel;
messageLabel.setText(message);
GridDataFactory
.fillDefaults()
.align(SWT.FILL, SWT.BEGINNING)
.grab(true, false)
.hint(
convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH),
SWT.DEFAULT).applyTo(messageLabel);
}
return composite;
}
然后我們添加一個打開對話框的方法: open(final int second,final String message) ,second表示倒計時時間,message表示提示信息,在open方法中新建一個 Timer 對象。對了,為了讓MessageDialog彈出后不阻塞線程的執行,需要在調用對話框的open方法之前調用 setBlockOnOpen(false); 方法,下面是我的實現,大家可以參考一下:
/**
* 打開定時關閉對話框,顧名思義,就是在指定的時間自動關閉 。
* @author 劉堯興
* @param second
* @param message
*/
public void open(final int second,final String message) {
setBlockOnOpen(false);
open();
// final MessageFormat format = new MessageFormat("系統檢查到重要插件的更新,系統將會在 {0} 秒后自動重啟,請注意保存文件");
timer = new Timer("檢查更新");
final TimerTask task = new TimerTask() {
int count = second;
public void run() {
Display display = Display.getDefault();
if (display == null) {
return;
}
display.asyncExec(new Runnable() {
public void run() {
try {
if (count == 0) {
cancel();
buttonPressed(Window.OK);
} else {
count -= 1;
if (!localMessageLabel.isDisposed()&&localMessageLabel != null) {
localMessageLabel.setText(message+"系統將在:" + count + "秒后自動重啟");
// localMessageLabel.setText(format.format(new Object[]{new Integer(count)}));
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
timer.schedule(task, 1000, 1000);
}
這里的Timer是每一秒鐘執行一次,在定時器執行完成后應該讓對話框自動關閉,然后可以通知事件發起人來執行對應的操作,從這樣設計的角度上看,我們的需要在用戶點擊確認或者取消或者關閉對話框時響應用戶的操作,這樣我們的重載父類的 buttonPressed(int buttonId) 方法,在這個方法中執行對應的操作.比如這樣:
protected void buttonPressed(int buttonId) {
if(buttonId == Window.OK) {
timer.cancel();
firePropertyChange(IPropery.RESTART_OK_PRESSED, false, true);
} else if(buttonId == Window.CANCEL) {
close();
}
}
這里我使用了java.util包中的 PropertyChangeListener 來監聽屬性值的修改。這里的對話框應該算是事件源,由這個發起屬性事件的修改通知,其他監聽類根據接收到的信息作出相應的響應。我在TimerMessageDialog實現了一個內部類: PropertyChangeSupportImpl ,這個類管理通知的對象:
/**
* 屬性修改支持實現類 。
* @author 劉堯興
* <p>2009-8-18</p>
*/
public class PropertyChangeSupportImpl {
/** 屬性修改支持 */
PropertyChangeSupport listeners = new PropertyChangeSupport(this);
/**
* 添加屬性修改事件 。
* @author 劉堯興
* @param l
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
listeners.addPropertyChangeListener(l);
}
/**
* 通知修改事件發生 。
* @author 劉堯興
* @param prop
* @param old
* @param newValue
*/
public void firePropertyChange(String prop, Object old, Object newValue) {
listeners.firePropertyChange(prop, old, newValue);
}
/**
* 通知事件修改 。
* @author 劉堯興
* @param prop
* @param child
*/
protected void fireStructureChange(String prop, Object child) {
listeners.firePropertyChange(prop, null, child);
}
/**
* 刪除屬性修改事件 。
* @author 劉堯興
* @param l
*/
public void removePropertyChangeListener(PropertyChangeListener l) {
listeners.removePropertyChangeListener(l);
}
}
在完成這些之后,在監聽類中注冊一個屬性修改的事件,比如在UpdatePlan這個類中執行TimerMessageDialog的打開操作,那就的讓UpdatePlan這個類實現 PropertyChangeListener 這個接口并實現 propertyChange(final PropertyChangeEvent evt) 這個方法,這個方法可以根據傳過來的屬性名稱和屬性值執行相應的操作。UpdatePlan屬性注冊方式是:
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
try {
TimerMessageDialog dialog = new TimerMessageDialog(Display.getDefault().getActiveShell(),"系統信息","系統將要自動重啟");
dialog.addPropertyChangeListener(this);
dialog.open(30,"");
}
catch (Exception e) {
e.printStackTrace();
}
}
});
因為我是在RCP的非UI現在調用這個對話框,所以需要在Display的asyncExec方法啟用一個新的異步線程.這樣就不會拋出非法的線程訪問異常了。
看下效果:
下面是完整的類
package com.cnex.oss.managerupdate.tools;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Timer;
import java.util.TimerTask;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
/**
* 定時執行對話框 。
* @author 劉堯興
* <p>2009-12-7</p>
*/
public class TimerMessageDialog extends MessageDialog2 {
/** 標簽文本 */
protected static Label localMessageLabel;
/** 屬性修改支持實現類 */
private PropertyChangeSupportImpl instance = new PropertyChangeSupportImpl();
/** 定時器 */
private Timer timer;
/**
* 構造函數。
* @param parentShell
* @param dialogTitle
* @param dialogMessage
*/
public TimerMessageDialog(Shell parentShell, String dialogTitle, String dialogMessage) {
super(parentShell, dialogTitle, null, dialogMessage, INFORMATION, new String[] {"立即重啟","隱藏"}, 0);
setShellStyle(SWT.BORDER | SWT.MIN);
}
protected Control createMessageArea(Composite composite) {
// create composite
// create image
Image image = getImage();
if (image != null) {
imageLabel = new Label(composite, SWT.NULL);
image.setBackground(imageLabel.getBackground());
imageLabel.setImage(image);
// addAccessibleListeners(imageLabel, image);
GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.BEGINNING)
.applyTo(imageLabel);
}
// create message
if (message != null) {
messageLabel = new Label(composite, getMessageLabelStyle());
TimerMessageDialog.localMessageLabel = messageLabel;
messageLabel.setText(message);
GridDataFactory
.fillDefaults()
.align(SWT.FILL, SWT.BEGINNING)
.grab(true, false)
.hint(
convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH),
SWT.DEFAULT).applyTo(messageLabel);
}
return composite;
}
/**
* 打開定時關閉對話框,顧名思義,就是在指定的時間自動關閉 。
* @author 劉堯興
* @param second
* @param message
*/
public void open(final int second,final String message) {
setBlockOnOpen(false);
open();
// final MessageFormat format = new MessageFormat("系統檢查到重要插件的更新,系統將會在 {0} 秒后自動重啟,請注意保存文件");
timer = new Timer("檢查更新");
final TimerTask task = new TimerTask() {
int count = second;
public void run() {
Display display = Display.getDefault();
if (display == null) {
return;
}
display.asyncExec(new Runnable() {
public void run() {
try {
if (count == 0) {
cancel();
buttonPressed(Window.OK);
} else {
count -= 1;
if (!localMessageLabel.isDisposed()&&localMessageLabel != null) {
localMessageLabel.setText(message+"系統將在:" + count + "秒后自動重啟");
// localMessageLabel.setText(format.format(new Object[]{new Integer(count)}));
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
timer.schedule(task, 1000, 1000);
}
protected void buttonPressed(int buttonId) {
if(buttonId == Window.OK) {
timer.cancel();
firePropertyChange(IPropery.RESTART_OK_PRESSED, false, true);
} else if(buttonId == Window.CANCEL) {
close();
}
}
/**
* 添加屬性事件 。
* @author 劉堯興
* @param l
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
instance.addPropertyChangeListener(l);
}
/**
* 刪除屬性修改事件 。
* @author 劉堯興
* @param l
*/
public void removePropertyChangeListener(PropertyChangeListener l) {
instance.removePropertyChangeListener(l);
}
/**
* 通知屬性修改事件(通知前臺代碼修改) 。
* @author 劉堯興
* @param prop
* @param old
* @param newValue
*/
public void firePropertyChange(String prop, Object old, Object newValue) {
instance.firePropertyChange(prop, old, newValue);
}
/**
* 屬性修改支持實現類 。
* @author 劉堯興
* <p>2009-8-18</p>
*/
public class PropertyChangeSupportImpl {
/** 屬性修改支持 */
PropertyChangeSupport listeners = new PropertyChangeSupport(this);
/**
* 添加屬性修改事件 。
* @author 劉堯興
* @param l
*/
public void addPropertyChangeListener(PropertyChangeListener l) {
listeners.addPropertyChangeListener(l);
}
/**
* 通知修改事件發生 。
* @author 劉堯興
* @param prop
* @param old
* @param newValue
*/
public void firePropertyChange(String prop, Object old, Object newValue) {
listeners.firePropertyChange(prop, old, newValue);
}
/**
* 通知事件修改 。
* @author 劉堯興
* @param prop
* @param child
*/
protected void fireStructureChange(String prop, Object child) {
listeners.firePropertyChange(prop, null, child);
}
/**
* 刪除屬性修改事件 。
* @author 劉堯興
* @param l
*/
public void removePropertyChangeListener(PropertyChangeListener l) {
listeners.removePropertyChangeListener(l);
}
}
}