注:以下是Swing GUI處理的一個小小技巧,對此無興趣者請退散。
在Swing中有這樣一種狀況:即長時間運行的事件回調,當它運行時,其余的GUI是沒有響應的。如果這會持續較長的一段時間,它可能會讓使用者感到挫折和困惑。下面一段程序就展示了這一現象,其粗體部分的本意是每隔一秒刷新標簽中的文字,但是結果是按鈕事件響應完畢后,標簽上顯示最后一段文字:
package com.heyang;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class MyFrame extends JFrame{
private static final long serialVersionUID = -5100794608937579830L;
private JLabel msgLbl;
private JButton cmdBtn;
public MyFrame(){
setTitle("MyFrame");
msgLbl=new JLabel("提示文字");
cmdBtn=new JButton("刷新文本");
this.setLayout(new BorderLayout());
this.add(msgLbl,BorderLayout.NORTH);
this.add(cmdBtn,BorderLayout.CENTER);
// 設置大小,位置
setSizeAndCentralizeMe(300, 200);
// 點擊窗口右上角的關閉按鈕關閉窗口,直接退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 按鈕事件注冊
cmdBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runCmd();
}
});
setVisible(true);
}
private void runCmd(){
msgLbl.setText("溫故而知新,可以為師矣。");
longTimeProcess(10);
msgLbl.setText("由,汝知之乎!知之為知之,不知為不知,是知也。");
longTimeProcess(10);
msgLbl.setText("見賢思齊焉,見不賢而內自省也");
longTimeProcess(10);
msgLbl.setText("士不可以不弘毅,任重而道遠。");
longTimeProcess(10);
msgLbl.setText("歲寒,然后知松柏之后凋也。");
longTimeProcess(10);
}
/**
* 模擬一個長時處理,以100毫秒為單位
*
* 說明:
* @param mSeconds
* 創建時間:2011-1-9 下午12:02:28
*/
private void longTimeProcess(int mSeconds){
try{
Thread.sleep(mSeconds*100);
}
catch(Exception e){
}
}
private void setSizeAndCentralizeMe(int width, int height) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setSize(width, height);
this.setLocation(screenSize.width / 2 - width / 2, screenSize.height
/ 2 - height / 2);
}
public static void main(String[] args){
new MyFrame();
}
}
要達到預期的效果,Swing建議:讓長時間運行的任務在獨立的線程中運行會好很多,這樣能夠讓GUI有適當響應。修改后的代碼如下:
package com.heyang;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class MyFrame extends JFrame{
private static final long serialVersionUID = -5100794608937579830L;
private JLabel msgLbl;
private JButton cmdBtn;
public MyFrame(){
setTitle("MyFrame");
msgLbl=new JLabel("提示文字");
cmdBtn=new JButton("刷新文本");
this.setLayout(new BorderLayout());
this.add(msgLbl,BorderLayout.NORTH);
this.add(cmdBtn,BorderLayout.CENTER);
// 設置大小,位置
setSizeAndCentralizeMe(300, 200);
// 點擊窗口右上角的關閉按鈕關閉窗口,直接退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 按鈕事件注冊
cmdBtn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runCmd();
}
});
setVisible(true);
}
private void runCmd(){
new Thread(){
public void run(){
msgLbl.setText("溫故而知新,可以為師矣。");
longTimeProcess(10);
msgLbl.setText("由,汝知之乎!知之為知之,不知為不知,是知也。");
longTimeProcess(10);
msgLbl.setText("見賢思齊焉,見不賢而內自省也");
longTimeProcess(10);
msgLbl.setText("士不可以不弘毅,任重而道遠。");
longTimeProcess(10);
msgLbl.setText("歲寒,然后知松柏之后凋也。");
longTimeProcess(10);
}
}.start();
}
/**
* 模擬一個長時處理,以100毫秒為單位
*
* 說明:
* @param mSeconds
* 創建時間:2011-1-9 下午12:02:28
*/
private void longTimeProcess(int mSeconds){
try{
Thread.sleep(mSeconds*100);
}
catch(Exception e){
}
}
private void setSizeAndCentralizeMe(int width, int height) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setSize(width, height);
this.setLocation(screenSize.width / 2 - width / 2, screenSize.height
/ 2 - height / 2);
}
public static void main(String[] args){
new MyFrame();
}
}
以上代碼達到了預期效果,其中起關鍵作用的代碼就是以上粗體部分,它將長時處理放到了另一個線程中運行。
這種做法不是唯一解決之道,Sun提供的SwingWorker類可以幫你達到目的,只是要繁瑣一些。這樣的技巧在耗時檢查,與服務器交互和復雜圖形處理中都能有所應用。
參考書籍:
O'REILLY 《Java 線程》一書。
最后感謝您看到這里。
何楊,2011年1月9日14:20:37