【问题标题】:Make a swing thread that show a "Please Wait" JDialog制作一个显示“请稍候”JDialog 的摆动线程
【发布时间】:2013-12-14 15:58:51
【问题描述】:

问题是这样的:
我有一个正在运行的摇摆应用程序,在某个时候对话框需要插入用户名和密码并按“确定”。
我希望当用户按“确定”时,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

我该怎么办?

【问题讨论】:

    标签: java multithreading swing event-dispatch-thread


    【解决方案1】:

    上述答案的变体

    这是一种简单且可复制的方式...

    //This code goes inside your button action   
    DialogWait wait = new DialogWait();
    
    SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>() {
    
        @Override
        protected Void doInBackground() throws Exception {
    
            //Here you put your long-running process...
    
            wait.close();
            return null;
        }
    };
    
    mySwingWorker.execute();
    wait.makeWait("Test", evt);
    //end
    
    
    //Create this class on your project
    class DialogWait {
    
    private JDialog dialog;
    
    public void makeWait(String msg, ActionEvent evt) {
    
        Window win = SwingUtilities.getWindowAncestor((AbstractButton) evt.getSource());
        dialog = new JDialog(win, msg, Dialog.ModalityType.APPLICATION_MODAL);
    
        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);
       }
    
       public void close() {
           dialog.dispose();
       }
    }
    

    【讨论】:

      【解决方案2】:

      考虑使用 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<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);
         }
      }
      

      注意事项:

      • 一个关键概念是设置所有内容,添加 PropertyChangeListener,让 SwingWorker 运行,所有显示模态对话框之前,因为一旦显示模态对话框,调用代码的所有代码流都被冻结(如您所见)。
      • 为什么我更喜欢 PropertyChangeListener 而不是使用 done 方法(正如 Elias 在他在这里的体面回答中所展示的那样,我对此表示赞同) - 使用侦听器提供了更多的关注点分离和更松散的耦合。这样,SwingWorker 就不必知道使用它的 GUI 代码。

      【讨论】:

      • 它似乎有效,谢谢(也感谢@Elias):)。但现在还有一个小问题。当我关闭主应用程序(使用 dispose() )时,SwingWorker 线程继续运行。我必须明确关闭它吗?到达doInBackground的return语句时不终止吗?
      • 如果executedialog.pack() 之前结束怎么办?也许应用程序可能会卡住?
      • @Hovercraft... 我确实对其进行了测试,似乎dispose() 等到setVisible(true) 被调用,因此没关系。
      • @VitBernatik 你能否澄清dispose() 是否保证等到setVisible(true) 被调用,如果是这样,记录在哪里?我在 docs.oracle.com/javase/7/docs/api/java/awt/… 中找不到任何内容
      • @beldaz:我只是在我的软件上进行测试。有一些长时间的延误。我不能保证它会在新版本中工作。
      【解决方案3】:
      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();
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-02-05
        • 2012-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多