您的问题在于您使用了Thread.sleep,因为当您在 Swing 事件线程(或事件调度线程的 EDT)上调用它时,它将使整个 Swing 事件线程进入睡眠状态。发生这种情况时,无法执行该线程的操作,包括绘制 GUI(更新它)和与用户交互,这将完全冻结您的 GUI——不好。当前情况下的解决方案是使用Swing Timer 作为伪循环。 Timer 在后台线程中创建一个循环,并保证其 actionPerformed 方法中的所有代码都将在 Swing 事件线程上调用,这是必要的,因为我们不想附加到该线程之外的 JTextArea。
正如其他人所指出的,如果您只想在 Swing 中执行重复动作并延迟,那么可以,使用此 Swing Timer。另一方面,如果您希望在 Swing 中运行一段长时间运行的代码,那么此代码将再次阻塞 EDT 并冻结您的程序。对于这种情况,请使用后台线程,例如 SwingWorker 提供的后台线程。请查看Lesson: Concurrency in Swing 了解更多信息。
例如,
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// delay between timer ticks: 1000
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
private int counter = 0;
@Override
public void actionPerformed(ActionEvent e) {
// timer's stopping condition
if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5
((Timer) e.getSource()).stop();
} else {
textArea.append("Value " + counter + "\n");
}
counter++; // increment timer's counter variable
}
}).start();
}
});
整件事:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import javax.swing.*;
public class SwingThread2 {
protected static final int MAX_VALUE = 5; // our constant
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SwingThread2 window = new SwingThread2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SwingThread2() {
initialize();
}
private void initialize() {
frame = new JFrame();
// frame.setBounds(100, 100, 450, 300); // avoid this
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JTextArea textArea = new JTextArea(15, 40);
scrollPane.setViewportView(textArea);
JButton btnNewButton = new JButton("New button");
scrollPane.setColumnHeaderView(btnNewButton);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// delay between timer ticks: 1000
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
private int counter = 0;
@Override
public void actionPerformed(ActionEvent e) {
// timer's stopping condition
if (counter >= MAX_VALUE) { // MAX_VALUE is a constant int = 5
((Timer) e.getSource()).stop();
} else {
textArea.append("Value " + counter + "\n");
}
counter++; // increment timer's counter variable
}
}).start();
}
});
// better to avoid setting sizes but instead to
// let the components size themselves vis pack
frame.pack();
frame.setLocationRelativeTo(null);
}
}
只是为了获得更多信息,这里是上面相同程序的示例,它使用 SwingWorker 执行长时间运行的操作,然后使用此操作更新 JProgressBar。 worker 非常简单,只需使用 while 循环将计数器变量推进有界随机量。然后它使用这个值来更新它自己的进度属性(这个值只能是从 0 到 100,所以在其他情况下,这个值需要被规范化以符合这个)。我将 PropertyChangeListener 附加到工作人员,并且每当工作人员的进度值更改以及 SwingWorker 更改状态时(例如完成操作时),都会在 Swing 事件线程上通知这一点。在后一种情况下,worker 的 StateValue 变为 StateValue.DONE。然后侦听器相应地更新 GUI。有什么问题请追问。
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
public class SwingThread2 {
protected static final int MAX_VALUE = 5; // our constant
private JFrame frame;
private JProgressBar progressBar = new JProgressBar();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SwingThread2 window = new SwingThread2();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SwingThread2() {
initialize();
}
private void initialize() {
frame = new JFrame();
// frame.setBounds(100, 100, 450, 300); // avoid this
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane();
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
JTextArea textArea = new JTextArea(15, 40);
scrollPane.setViewportView(textArea);
JButton btnNewButton = new JButton("New button");
scrollPane.setColumnHeaderView(btnNewButton);
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// delay between timer ticks: 1000
int timerDelay = 1000;
new Timer(timerDelay, new ActionListener() {
private int counter = 0;
@Override
public void actionPerformed(ActionEvent e) {
// timer's stopping condition
if (counter >= MAX_VALUE) { // MAX_VALUE is a constant
// int = 5
((Timer) e.getSource()).stop();
} else {
textArea.append("Value " + counter + "\n");
}
counter++; // increment timer's counter variable
}
}).start();
}
});
progressBar.setStringPainted(true);
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS));
bottomPanel.add(new JButton(new MyAction("Press Me")));
bottomPanel.add(progressBar);
frame.getContentPane().add(bottomPanel, BorderLayout.PAGE_END);
// better to avoid setting sizes but instead to
// let the components size themselves vis pack
frame.pack();
frame.setLocationRelativeTo(null);
}
private class MyAction extends AbstractAction {
public MyAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
public void actionPerformed(ActionEvent e) {
progressBar.setValue(0);
setEnabled(false);
MyWorker myWorker = new MyWorker();
myWorker.addPropertyChangeListener(new WorkerListener(this));
myWorker.execute();
}
}
private class WorkerListener implements PropertyChangeListener {
private Action action;
public WorkerListener(Action myAction) {
this.action = myAction;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
int progress = (int) evt.getNewValue();
progressBar.setValue(progress);
} else if ("state".equals(evt.getPropertyName())) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
action.setEnabled(true);
@SuppressWarnings("rawtypes")
SwingWorker worker = (SwingWorker) evt.getSource();
try {
// always want to call get to trap and act on
// any exceptions that the worker might cause
// do this even though get returns nothing
worker.get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
}
private class MyWorker extends SwingWorker<Void, Void> {
private static final int MULTIPLIER = 80;
private int counter = 0;
private Random random = new Random();
@Override
protected Void doInBackground() throws Exception {
while (counter < 100) {
int increment = random.nextInt(10);
Thread.sleep(increment * MULTIPLIER);
counter += increment;
counter = Math.min(counter, 100);
setProgress(counter);
}
return null;
}
}
}