制作一个显示“请稍候”JDialog 的摆动线程

问题是这样的:
我有一个swing应用程序正在运行,在某个时刻,对话框需要插入用户名和密码,然后按“ok”。
我希望当用户按“ok”时,Swing应用程序按以下顺序执行:

  1. 打开“请稍候”JDialog
  2. 进行一些操作(最终显示其他一些JDialog或JOptionPane)
  3. 当它完成操作关闭“请稍候”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 in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher thread

我该怎么办?


答案 1

请考虑使用 SwingWorker 执行后台工作,然后在 SwingWorker 的方法中关闭对话框,或者在添加到 SwingWorker 的 PropertyChangeListener 中关闭对话框(我的首选项)。done()

例如,

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<Void, Void> mySwingWorker = new SwingWorker<Void, Void>(){
         @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);
   }
}

笔记:

  • 一个关键的概念是设置所有内容,添加属性更改Listener,运行SwingWorker,所有这些都在显示模式对话框之前完成,因为一旦显示模式对话框,来自调用代码的所有代码流都会被冻结(正如您已经发现的那样)。
  • 为什么我更喜欢 PropertyChangeListener 而不是使用 done 方法(正如 Elias 在他这里给出的体面答案中演示的那样,我已经投了赞成票)——使用侦听器提供了更多的关注点分离,更松散的耦合。这样,SwingWorker 就不必知道使用它的 GUI 代码。

答案 2
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<String, Void> worker = new SwingWorker<String, Void>() {
        @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();
    }
}