我有一个java swing GUI,我正在尝试实现倒数计时器。我有一系列任务,每个任务都需要在for循环中顺序运行。我已经能够成功地使用摇摆计时器来进行倒数计时器,但是一旦我尝试将它放入循环中,我就无法在不冻结GUI的情况下进行更新。
这是我目前工作的SSCCE。基本问题是这个例子并行运行三个30秒计时器。我想要做的是按顺序运行三个30秒计时器,换句话说它应该等待一个计时器完成,然后再开始下一个计时器。我尝试等待计时器完成所做的一切都会使GUI冻结。
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.security.auth.callback.Callback;
import javax.swing.*;
public class SwingTester extends JFrame {
public SwingTester() {
JPanel panel = new JPanel();
getContentPane().add(panel);
panel.setLayout(new GridLayout(1,3));
setDefaultCloseOperation(EXIT_ON_CLOSE);
// Add the components
jLblTimer = new JLabel("00:30");
jLblSet = new JLabel("Set #1");
jBtnStart = new JButton("Start!");
jBtnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {jBtnStartActionPerformed(evt);}
});
// Add the labels
panel.add(jLblTimer); panel.add(jLblSet); panel.add(jBtnStart);
pack();
}
private void jBtnStartActionPerformed(ActionEvent evt) {
for (int ss=0; ss<3; ss++) {
// Change the set name
jLblSet.setText("Set #" + Integer.toString(ss+1));
// Setup the counter to begin the countdown
counter = 30;
timer = new Timer(1000, startCycle());
timer.start();
// I want to wait here until the timer is done, nothing I've done
// works without freezing the GUI.
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new SwingTester().setVisible(true);
}
});
}
private Action startCycle() {
return new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
new ClockTask(null).execute();
}
};
}
class ClockTask extends SwingWorker<Void, String> {
private Callback callback;
public ClockTask(Callback callback) {
this.callback = callback;
}
@Override
protected Void doInBackground() throws Exception {
if (counter >= 0) {
int millis = counter*1000;
String time = String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(millis),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);
publish(time); // call publish which will call process() method in EDT
counter--;
} else {
// this.callback.call();
this.callback.notify();
timer.stop();
}
return(null);
}
@Override
protected void process(List<String> times) {
String lastTime = times.get(times.size()-1);
jLblTimer.setText(lastTime); // just update ui with lastTimer, ohters are ignorable
}
@Override
protected void done() {
super.done();
}
// private interface Callback {
// void call();
// }
}
private javax.swing.JLabel jLblTimer;
private javax.swing.JLabel jLblSet;
private javax.swing.JButton jBtnStart;
private javax.swing.Timer timer;
private int counter;
}
我也尝试使用sleep函数等到倒计时结束,但这会阻止GUI更新。 任何建议都是最受欢迎的。你可以从我的代码中看出我不是程序员。
答案 0 :(得分:3)
以下是您需要更改SwingWorker的方法
class ClockTask extends SwingWorker<Void, String> {
private Callback callback;
public ClockTask(Callback callback) {
this.callback = callback;
}
@Override
protected Void doInBackground() throws Exception {
if (counter >= 0) {
int millis = counter*1000;
String time = String.format("%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(millis),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);
//jLblTimer.setText(time); // dont update UI in backgroudn thread
publish(time); // call publish which will call process() method in EDT
counter--;
} else {
this.callback.call();
}
return(null);
}
/*
* this method will be called in EDT and will not freeze your UI ;)
*/
@Override
protected void process(List<String> times) {
String lastTime = times.get(times.size()-1);
jLblTimer.setText(lastTime); // just update ui with lastTimer, ohters are ignorable
}
@Override
protected void done() {
super.done();
}
private interface Callback {
void call();
}
}
您应该花费更多时间使用SwingWorker
的JavaDoc答案 1 :(得分:-2)
有几种方法可以做到这一点。通常你可以创建一个对象,将它传递给后台工作,在后台工作完成后,它使用该对象告诉你结果并让你完成其余的工作。
private class ClockTask extends SwingWorker<Void, Void> {
private Callback _callback;
public ClockTask(Callback callback) {
this._callback = callback;
}
@Override
protected Void doInBackground() throws Exception {
if (counter >= 0) {
int millis = counter*1000;
String time = String.format("%02d:%02d",
TimeUnit.MILLISECONDS.toMinutes(millis),
TimeUnit.MILLISECONDS.toSeconds(millis) -
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
);
jLblTimer.setText(time);
counter--;
} else {
this._callback.notify();
}
return(null);
}
}