注:以下是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