我最初尝试在Java Action Listener中多次更新JFrame和JPanel,但两者都只会在Action Listener完成所有任务时更新。这是我原始问题(Refreshing a JFrame while in an Action Listener)的链接。
我在反馈中告诉我,Swing Worker应该解决我的问题。但是,当我实现Swing Worker(如下所示)时,没有任何改变。仅当Action Listener完成所有任务时,JFrame和JPanel仍会更新。我的问题是,我错过了下面的内容吗?如果没有,我如何在Action Listener中实现它以及时正确更新Frame和Panel?
@Override
protected Integer doInBackground() throws Exception{
//Downloads and unzips the first video.
if(cameraBoolean==true)
panel.add(this.downloadRecording(camera, recording));
else
panel.add(new JLabel("Could not contact camera "+camera.getName()));
panel.repaint();
jframe.repaint();
return 1;
}
private JLabel downloadRecording(Camera camera, Recording recording){
//does a bunch of calculations and returns a jLabel, and works correctly
}
protected void done(){
try{
Date currentTime = new Timestamp(Calendar.getInstance().getTime().getTime());
JOptionPane.showMessageDialog(jframe, "Camera "+camera.getName()+" finished downloading at "+currentTime.getTime());
}catch (Exception e){
e.printStackTrace();
}
}
答案 0 :(得分:5)
您对SwingWorker
的工作方式存在误导。此类旨在提供在执行繁重任务时更新GUI的方法。所有这一切都是因为Swing
组件更新发生在Event Dispatch Thread(a.k.a。EDT)中,这是一个特定的线程。
例如,如果单击一个按钮并在EDT中执行所有耗时的任务,则此线程将阻止直到此任务完成。因此,您将看到您的GUI被冻结。
记住这一点,doInBackground()方法在另一个不是EDT的不同线程中运行,这是正常的。所以不要在那里调用任何Swing
方法:
protected Integer doInBackground() throws Exception{
//Downloads and unzips the first video.
if(cameraBoolean==true) // just use if(cameraBoolean), since this is a boolean
panel.add(this.downloadRecording(camera, recording)); // NO!
else
panel.add(new JLabel("Could not contact camera "+camera.getName())); //NO!
panel.repaint(); //NO, never!
jframe.repaint();//NO, never!
return 1;
}
在执行JLabel
之前向此panel
添加SwingWorker
,并使用publish()和process()方法更新其文字:
JPanel panel = new JPanel();
final JLabel progressLabel = new JLabel("Some text before executing SwingWorker");
panel.add(progressLabel);
SwingWorker<Integer, String> worker = new SwingWorker<Integer, String>() {
@Override
protected Integer doInBackground() throws Exception {
if(cameraBoolean){
pubish("Starting long process...");
//Some processing here
publish("Intermediate result to be published #1");
//Some other processing stuff
publish("Intermediate result to be published #2");
//And so on...
return 0;
} else {
publish("Could not contact camera "+camera.getName());
return -1;
}
}
@Override
protected void process(List<String> chunks) {
for(String string : chunks){
progressLabel.setText(string);
}
}
@Override
protected void done() {
progressLabel.setText("Finished!!!");
}
};
worker.execute();
process()
和done()方法都在EDT中进行,因此可以安全地进行GUI更新。请查看这个极好的示例:Swing Worker Example了解更多详情。
答案 1 :(得分:0)
可能是因为您在同步通话this.downloadRecording(camera, recording)
完成时重新绘制了面板/框架?
尝试仅将此调用放入doInBackground()
方法,因为(我猜)这是一个花费很长时间的时间,并且所有这些时间JFrame都没有刷新。
答案 2 :(得分:0)
您无法以下一种方式更新用户界面:
panel.repaint();
jframe.repaint();
在doInBackground
方法中,您必须调用publish(V... chunks)
方法Sends data chunks to the process(java.util.List<V>) method.
(根据文档)而不是方法process(List<V> chunks)
,您可以更新您的用户界面(根据文档流程)方法 - Receives data chunks from the publish method asynchronously on the Event Dispatch Thread.
)。 SwingWorker docs.
因此,覆盖process
方法进行更新,并调用publish
方法。
您也可以使用执行者进行后台处理。在这种情况下,您的UI将在EDT中工作,您的后台进程将在另一个线程中工作。例如:
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
// run background process
}
});