窗口组件动态刷新问题,在dotnet中根本不算什么问题,用几句代码很轻松就能搞定,但是在Swing中,实现动态刷新组件内容却是一件颇为吃力的事情。譬如针对我们经常用到的刷新JLable、JTextField等组件内容,这些组件本身也提供有诸如updateUI()、validate()以及paintImmediately()等等与界面刷新和组件重绘相关的方法, 但是在常规情况下都不起作用。
我们先看个基本的例子,JLabel内容动态刷新的例子,代码如下。
try {
for(int i=0;i<5;i++) {
this.lbInfo.setText(String.format("当前进度:%s", i));
this.lbInfo.paintImmediately(this.lbInfo.getBounds());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
我们在一个for循环内每间隔一秒钟刷新一下Label内容,以期望实现Label内容动态刷新效果,但是并没有出现我们想象中的效果,Label内容一直保持不变,直到最后才显示最终的效果,中间的动态内容完全被忽略过去了。这是因为每次调用setText都是运行在主线程中,而且是顺序的执行的。在前面几次调用setText后,线程并没有退出,所以界面刷新线程不能获得执行刷新的机会。而当最后一次setText后,线程退出,界面才能执行刷新。所以我们只能看到最后一次setText的值。
原来,在Java Swing中,界面刷新是线程同步的,也即是说,在同一时间,只有一个线程能够执行刷新界面的代码。如果要多次不断地刷新界面,必须在多线程中调用刷新的方法。我们将以上的代码更改为多线程模式,重新运行,结果就完全实现了我们所期望的动态刷新效果。代码如下。
new Thread(new Runnable(){
@Override
public void run() {
try {
for(int i=0;i<5;i++) {
lbInfo.setText(String.format("当前进度:%s", i));
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
那么,除了这种采用显式多线程模式外,还有没有其他方法实现内容动态刷新效果呢?我们进行了测试,还真的找到了一种方法,就是结合JScrollPane实现及时动态刷新。在窗体区域创建一个JScrollPane组件,然后将JLabel组件放入JScrollPane中,执行以下代码,同样可以实现内容及时动态刷新。代码如下:
try {
for(int i=0;i<5;i++) {
this.lbInfo.setText(String.format("当前进度:%s", i));
this.lbInfo.paintImmediately(this.lbInfo.getBounds());
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
做一个小小的总结,针对JLableJ、TextField和JTextArea等组件内容的及时动态刷新问题,基本上采用多线程或者JScrollPane方式都可以实现。但是,对于JTextArea组件,如果不采用多线程模式,在内容动态刷新的时候,JTextArea的滚动条将无法与内容区域保持同步。所以最稳妥可靠的做法,就是采用多线程模式实现内容的及时动态刷新。在下一章节中,我们将利用JScrollPane、JTextArea、多线程和Java自定义事件机制,设计实现一个“日志信息前台监控器”,利用前台窗体实时监控显示后台服务的日志信息。
作者:商兵兵
单位:河南省电力科学研究院智能电网所
QQ:52190634