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

Posted

技术标签:

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

问题是这样的: 我有一个正在运行的摇摆应用程序,在某个时候对话框需要插入用户名和密码并按“确定”。 我希望当用户按“确定”时,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 in thread "AWT-EventQueue-0" java.lang.Error: Cannot call invokeAndWait from the event dispatcher 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();
    

【讨论】:

以上是关于制作一个显示“请稍候”JDialog 的摆动线程的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Winforms 中显示“正在加载...请稍候”消息以获取长加载表单?

如何在流程完成之前显示“请稍候 gif”图像

Streamlit 应用程序以“请稍候...”停止,然后停止

“我很忙,请稍候”窗口 - 没有按钮

有啥办法可以刷新 JDialog 并重新制作它?

QQ不能添加好友,“显示您最近操作频繁或账户存在不安全因素,已被系统锁定一段时间,请稍候再试。”