我试图通过不在主线程(EDT)上执行长任务来遵循Java最佳实践。因此,我计划将“ swingWorker”与“模态对话框”一起使用。这样,模式对话框会阻止用户执行任何操作,直到该任务完成为止,并且我可以在过程进行时更新对话框上的状态。
现在的问题是,使用模式对话框,它不仅会阻止用户,而且在调用setVisible之后也不会阻止用户
所以如果我这样做
dialog.setVisible(true); new SwingWorkerTask().execute(); //This does not get called
如果我愿意
new SwingWorkerTask().execute(); dialog.setVisible(true); // Well what the point of setting visible after the fact.
那么,如何在执行任务时阻止用户操作并显示对话框?
谢谢
如果您这样做的话,那只是鸡/蛋。您可以在EDT上构造所有Swing对象,然后SwingWorker通过指示EDT通过执行它们来让您(或任何其他线程)管理所有更新SwingUtilities.invokeLater(Runnable)。
SwingWorker
SwingUtilities.invokeLater(Runnable)
import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JProgressBar; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; public class RudeProgressBar extends JFrame { private JButton button; public RudeProgressBar() { setTitle("Rude Progress Bar"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLayout(new BorderLayout()); button = new JButton("Do teh work"); add(button, BorderLayout.SOUTH); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JDialog dialog = new JDialog(RudeProgressBar.this, true); dialog.setTitle("Doing teh work"); dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); final JProgressBar progressBar = new JProgressBar(0, 100); dialog.setLayout(new BorderLayout()); dialog.add(progressBar); dialog.setSize(100, 100); dialog.setLocationRelativeTo(RudeProgressBar.this); MyTask task = new MyTask(dialog); task.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ("progress".equals(evt.getPropertyName())) { progressBar.setValue((Integer)evt.getNewValue()); } } }); task.execute(); } }); setSize(200, 200); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new RudeProgressBar().setVisible(true); } }); } private class MyTask extends SwingWorker<Void, Void> { private final JDialog dialog; public MyTask(JDialog dialog) { this.dialog = dialog; } @Override protected Void doInBackground() throws Exception { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { dialog.setVisible(true); } }); int progress = 0; for (int i = 0; i < 5; i++) { Thread.sleep(1000); setProgress(progress += 20); } return null; } @Override protected void done() { dialog.setVisible(false); dialog.dispose(); } } }
如果您担心invokeLater实现(内部SwingWorker.doInBackground)在执行之后会被执行SwingWorker.done,只需将代码done放入另一个invokeLater。通过这样做,您可以将Runnable实现排入队列以让EDT以特定顺序执行它们。即使从EDT本身调用此方法,也会发生排队。
invokeLater
SwingWorker.doInBackground
SwingWorker.done
done
Runnable
请注意,如果您看一下SwingWorker实现,您会看到它依赖于javax.swing.Timer执行done()和Timer本身调用invokeLater,因此done再次在内部调用它无济于事。但是,如果这样做,不会有任何问题。
javax.swing.Timer
done()
Timer