Swing 使用单线程模型来进行事件分派(包括绘制更新),因此,您永远不应该在事件分派线程 (EDT) 内执行任何长时间运行或阻塞的操作。
Swing 也不是线程安全的,这意味着您永远不应该从 EDT 外部创建或修改任何 UI 组件。
全局进度对话框的基本概念是提供一个不依赖于需要执行的工作的框架,这将责任区域分开,进度对话框负责显示进度,而工作人员/引擎负责做实际工作。
最简单的解决方案是使用SwingWorker
。这提供了在单独的线程中执行长时间运行的任务、状态更改和进度通知的机制。
public class TestProgressDialog {
public static void main(String[] args) {
new TestProgressDialog();
}
public TestProgressDialog() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
SwingWorker worker = new SwingWorker() {
@Override
protected Object doInBackground() throws Exception {
for (int index = 0; index < 100; index++) {
Thread.sleep(50);
setProgress(index);
}
return null;
}
};
ProgressDialog.showProgress(null, worker);
System.exit(0);
}
});
}
public static class ProgressDialog extends JDialog {
private JLabel message;
private JLabel subMessage;
private JProgressBar progressBar;
public ProgressDialog(Component parent, SwingWorker worker) {
super(parent == null ? null : SwingUtilities.getWindowAncestor(parent));
setModal(true);
((JComponent)getContentPane()).setBorder(new EmptyBorder(8, 8, 8, 8));
message = new JLabel("Doing important stuff");
subMessage = new JLabel("Go make you're self a cup of coffee...");
progressBar = new JProgressBar();
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.insets = new Insets(2, 2, 2, 2);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.WEST;
add(message, gbc);
gbc.gridy++;
add(subMessage, gbc);
gbc.gridy++;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.HORIZONTAL;
add(progressBar, gbc);
pack();
worker.addPropertyChangeListener(new PropertyChangeHandler());
switch (worker.getState()) {
case PENDING:
worker.execute();
break;
}
}
public static void showProgress(Component parent, SwingWorker worker) {
ProgressDialog dialog = new ProgressDialog(parent, worker);
dialog.setLocationRelativeTo(parent);
dialog.setVisible(true);
}
public class PropertyChangeHandler implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("state")) {
SwingWorker.StateValue state = (SwingWorker.StateValue) evt.getNewValue();
switch (state) {
case DONE:
dispose();
break;
}
} else if (evt.getPropertyName().equals("progress")) {
progressBar.setValue((int)evt.getNewValue());
}
}
}
}
}