制作一个显示“请等待”JDialog的摇摆线程
问题是这样的:
我正在运行一个swing应用程序,在某个时刻,对话框需要插入用户名和密码并按“确定”。
我希望当用户按“确定”时,swing应用程序按此顺序执行:
- 打开“请等待”JDialog
- 进行一些操作(最终显示其他一些JDialog或JOptionPane)
- 当它完成操作时关闭“请等待”JDialog
这是我在okButtonActionPerformed()中编写的代码:
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) { //This class simply extends a JDialog and contains an image and a jlabel (Please wait) final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false); waitDialog.setVisible(true); ... //Do some operation (eventually show other JDialogs or JOptionPanes) waitDialog.dispose() }
这段代码显然不起作用,因为当我在同一个线程中调用waitDialog时,它会阻塞所有代码直到我不关闭它。
所以我试着在另一个线程中运行它:
private void okButtonActionPerformed(java.awt.event.ActionEvent evt) { //This class simply extends a JDialog and contains an image and a jlabel (Please wait) final WaitDialog waitDialog = new WaitDialog(new javax.swing.JFrame(), false); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { waitDialog.setVisible(true); } }); ... //Do some operation (eventually show other JDialogs or JOptionPanes) waitDialog.dispose() }
但这也不起作用,因为waitDialog不会立即显示,但只有在操作完成后才能完成工作(当他们显示joption窗格时“你以…登录”)
我还尝试使用invokeAndWait而不是invokeLater,但在这种情况下它会抛出exception:
Exception in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread
我能怎么做?
考虑使用SwingWorker进行后台工作,然后在SwingWorker的done()
方法或(我的首选项)中添加到SwingWorker的PropertyChangeListener中关闭对话框。
例如,
import java.awt.BorderLayout; import java.awt.Dialog.ModalityType; import java.awt.Window; import java.awt.event.ActionEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; public class PleaseWaitEg { public static void main(String[] args) { JButton showWaitBtn = new JButton(new ShowWaitAction("Show Wait Dialog")); JPanel panel = new JPanel(); panel.add(showWaitBtn); JFrame frame = new JFrame("Frame"); frame.getContentPane().add(panel); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } } class ShowWaitAction extends AbstractAction { protected static final long SLEEP_TIME = 3 * 1000; public ShowWaitAction(String name) { super(name); } @Override public void actionPerformed(ActionEvent evt) { SwingWorker mySwingWorker = new SwingWorker(){ @Override protected Void doInBackground() throws Exception { // mimic some long-running process here... Thread.sleep(SLEEP_TIME); return null; } }; Window win = SwingUtilities.getWindowAncestor((AbstractButton)evt.getSource()); final JDialog dialog = new JDialog(win, "Dialog", ModalityType.APPLICATION_MODAL); mySwingWorker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals("state")) { if (evt.getNewValue() == SwingWorker.StateValue.DONE) { dialog.dispose(); } } } }); mySwingWorker.execute(); JProgressBar progressBar = new JProgressBar(); progressBar.setIndeterminate(true); JPanel panel = new JPanel(new BorderLayout()); panel.add(progressBar, BorderLayout.CENTER); panel.add(new JLabel("Please wait......."), BorderLayout.PAGE_START); dialog.add(panel); dialog.pack(); dialog.setLocationRelativeTo(win); dialog.setVisible(true); } }
笔记:
- 一个关键的概念是设置所有内容,添加PropertyChangeListener,运行SwingWorker,所有这些都在显示模式对话框之前 ,因为一旦显示模式对话框,所有来自调用代码的代码流都被冻结(如您所知) 。
- 为什么我更喜欢PropertyChangeListener来使用done方法(正如Elias在这里得到的体面答案所示,我已经投票了) – 使用监听器提供了更多的关注点分离,更松散的耦合。 这样,SwingWorker就不必知道正在使用它的GUI代码。
public void okButtonActionPerformed(ActionEvent e) { final JDialog loading = new JDialog(parentComponent); JPanel p1 = new JPanel(new BorderLayout()); p1.add(new JLabel("Please wait..."), BorderLayout.CENTER); loading.setUndecorated(true); loading.getContentPane().add(p1); loading.pack(); loading.setLocationRelativeTo(parentComponent); loading.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); loading.setModal(true); SwingWorker worker = new SwingWorker() { @Override protected String doInBackground() throws InterruptedException /** Execute some operation */ } @Override protected void done() { loading.dispose(); } }; worker.execute(); loading.setVisible(true); try { worker.get(); } catch (Exception e1) { e1.printStackTrace(); } }