是不是覺得Windows的倒計(jì)時(shí)關(guān)機(jī)對(duì)話框很酷?

其實(shí)你也可以通過SWT的對(duì)話框來實(shí)現(xiàn)這個(gè)功能,如果你真的夠無聊的話,可以用來嚇唬一下你的用戶,當(dāng)然你的確定你的人緣很好才行.
好了,開場(chǎng)白到此為止,下面進(jìn)入正題:
我設(shè)計(jì)這個(gè)倒計(jì)時(shí)對(duì)話框主要是為了讓我們系統(tǒng)在自動(dòng)更新完成后自動(dòng)重啟,我們的系統(tǒng)提供了自動(dòng)更新的功能,而且有個(gè)數(shù)據(jù)傳輸模塊,數(shù)據(jù)傳輸模塊是使用Wrapper這個(gè)工具封裝Java程序成服務(wù),數(shù)據(jù)傳輸模塊會(huì)共享RCP客戶端的插件Jar包,由于Eclipse插件的特殊性,自動(dòng)更新完成后可能會(huì)在安裝目錄中存在同名稱但不同版本號(hào)的插件,具體細(xì)節(jié)省去7443個(gè)字節(jié)。
想實(shí)現(xiàn)倒計(jì)時(shí)對(duì)話框,我們可以使用Timer這個(gè)定時(shí)工具類,這主要考慮到倒計(jì)時(shí)對(duì)話框要和RCP項(xiàng)目的主線程區(qū)分開來,對(duì)話框彈出不能夠影響用戶的操作,至少得讓他們的有時(shí)間保存信息不至于丟失,我們把這個(gè)類命名為 TimerMessageDialog .java, 根據(jù)MessageDialog中提供的方法,我們無法動(dòng)態(tài)顯示提示信息,比如還剩下多少時(shí)間.你可以重載MessageDialog的 createMessageArea(Composite composite) 方法,可以再TimerMessageDialog中新增一個(gè)字段引用 messageLabel ,比如 localMessageLabel ,然后對(duì) localMessageLabel 的值進(jìn)行修改,下面是我的實(shí)現(xiàn),可以參考一下:
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;
}
然后我們添加一個(gè)打開對(duì)話框的方法: open(final int second,final String message) ,second表示倒計(jì)時(shí)時(shí)間,message表示提示信息,在open方法中新建一個(gè) Timer 對(duì)象。對(duì)了,為了讓MessageDialog彈出后不阻塞線程的執(zhí)行,需要在調(diào)用對(duì)話框的open方法之前調(diào)用 setBlockOnOpen(false); 方法,下面是我的實(shí)現(xiàn),大家可以參考一下:
/**
* 打開定時(shí)關(guān)閉對(duì)話框,顧名思義,就是在指定的時(shí)間自動(dòng)關(guān)閉 。
* @author 劉堯興
* @param second
* @param message
*/
public void open(final int second,final String message) {
setBlockOnOpen(false);
open();
// final MessageFormat format = new MessageFormat("系統(tǒng)檢查到重要插件的更新,系統(tǒng)將會(huì)在 {0} 秒后自動(dòng)重啟,請(qǐng)注意保存文件");
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+"系統(tǒng)將在:" + count + "秒后自動(dòng)重啟");
// localMessageLabel.setText(format.format(new Object[]{new Integer(count)}));
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
timer.schedule(task, 1000, 1000);
}
這里的Timer是每一秒鐘執(zhí)行一次,在定時(shí)器執(zhí)行完成后應(yīng)該讓對(duì)話框自動(dòng)關(guān)閉,然后可以通知事件發(fā)起人來執(zhí)行對(duì)應(yīng)的操作,從這樣設(shè)計(jì)的角度上看,我們的需要在用戶點(diǎn)擊確認(rèn)或者取消或者關(guān)閉對(duì)話框時(shí)響應(yīng)用戶的操作,這樣我們的重載父類的 buttonPressed(int buttonId) 方法,在這個(gè)方法中執(zhí)行對(duì)應(yīng)的操作.比如這樣:
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 來監(jiān)聽屬性值的修改。這里的對(duì)話框應(yīng)該算是事件源,由這個(gè)發(fā)起屬性事件的修改通知,其他監(jiān)聽類根據(jù)接收到的信息作出相應(yīng)的響應(yīng)。我在TimerMessageDialog實(shí)現(xiàn)了一個(gè)內(nèi)部類: PropertyChangeSupportImpl ,這個(gè)類管理通知的對(duì)象:
/**
* 屬性修改支持實(shí)現(xiàn)類 。
* @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);
}
/**
* 通知修改事件發(fā)生 。
* @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);
}
}
在完成這些之后,在監(jiān)聽類中注冊(cè)一個(gè)屬性修改的事件,比如在UpdatePlan這個(gè)類中執(zhí)行TimerMessageDialog的打開操作,那就的讓UpdatePlan這個(gè)類實(shí)現(xiàn) PropertyChangeListener 這個(gè)接口并實(shí)現(xiàn) propertyChange(final PropertyChangeEvent evt) 這個(gè)方法,這個(gè)方法可以根據(jù)傳過來的屬性名稱和屬性值執(zhí)行相應(yīng)的操作。UpdatePlan屬性注冊(cè)方式是:
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
try {
TimerMessageDialog dialog = new TimerMessageDialog(Display.getDefault().getActiveShell(),"系統(tǒng)信息","系統(tǒng)將要自動(dòng)重啟");
dialog.addPropertyChangeListener(this);
dialog.open(30,"");
}
catch (Exception e) {
e.printStackTrace();
}
}
});
因?yàn)槲沂窃赗CP的非UI現(xiàn)在調(diào)用這個(gè)對(duì)話框,所以需要在Display的asyncExec方法啟用一個(gè)新的異步線程.這樣就不會(huì)拋出非法的線程訪問異常了。
看下效果:
下面是完整的類
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;
/**
* 定時(shí)執(zhí)行對(duì)話框 。
* @author 劉堯興
* <p>2009-12-7</p>
*/
public class TimerMessageDialog extends MessageDialog2 {
/** 標(biāo)簽文本 */
protected static Label localMessageLabel;
/** 屬性修改支持實(shí)現(xiàn)類 */
private PropertyChangeSupportImpl instance = new PropertyChangeSupportImpl();
/** 定時(shí)器 */
private Timer timer;
/**
* 構(gòu)造函數(shù)。
* @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;
}
/**
* 打開定時(shí)關(guān)閉對(duì)話框,顧名思義,就是在指定的時(shí)間自動(dòng)關(guān)閉 。
* @author 劉堯興
* @param second
* @param message
*/
public void open(final int second,final String message) {
setBlockOnOpen(false);
open();
// final MessageFormat format = new MessageFormat("系統(tǒng)檢查到重要插件的更新,系統(tǒng)將會(huì)在 {0} 秒后自動(dòng)重啟,請(qǐng)注意保存文件");
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+"系統(tǒng)將在:" + count + "秒后自動(dòng)重啟");
// 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);
}
/**
* 通知屬性修改事件(通知前臺(tái)代碼修改) 。
* @author 劉堯興
* @param prop
* @param old
* @param newValue
*/
public void firePropertyChange(String prop, Object old, Object newValue) {
instance.firePropertyChange(prop, old, newValue);
}
/**
* 屬性修改支持實(shí)現(xiàn)類 。
* @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);
}
/**
* 通知修改事件發(fā)生 。
* @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);
}
}
}