【问题标题】:Modal JDialog without blocking execution不阻塞执行的模态 JDialog
【发布时间】:2014-07-23 16:27:30
【问题描述】:

有没有办法在 Swing 中使用一个对话框来禁止其下的任何 gui 活动,但同时不会停止在设置为可见的线程上执行?

【问题讨论】:

  • 没有。模态对话框控制事件队列并过滤分派的事件。你想达到什么目的。模态对话框应该只在 EDT 的上下文中使用,并且从不从任何其他线程调用,所以它应该只阻塞 EDT
  • 在其上方打开一个阻塞模式对话框时让动画继续播放是微不足道的,而且看起来它们在对话框打开时需要活动的其他东西很少,所以我看不到您的询问点。当然,如果对话框中的某个操作影响了被阻止的 GUI,那么该更改将毫无问题地继续进行。

标签: java swing modal-dialog jdialog


【解决方案1】:

是的,可以做到。

dlg.setModal(false);

dlg.setModalityType(Dialog.ModalityType.MODELESS);

其中 dlg 是您的 JDialog 的实例。

【讨论】:

  • 它不会阻止 GUI 活动。
【解决方案2】:

JDialog 的基本思想是阻塞底层线程,直到用户对其做出反应。如果您需要在不应中断的 UI 线程上运行某些内容,请考虑为其使用额外的工作线程。这样,UI 会被 JDialog 阻塞,但底层进程不会。

【讨论】:

    【解决方案3】:

    是的,有一个小技巧可以让它发挥作用。我们只需停用模态并手动禁用我们想要使其不可点击的JFrame

    private final static JDialog dialog; static {
            JOptionPane pane = new JOptionPane();
            pane.setOptions(new Object[]{}); // Removes all buttons
            dialog = pane.createDialog(frame, ""); // Create dialog with pane
            dialog.setModal(false); // IMPORTANT! Now the thread isn't blocked
            dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
    }
    

    现在你可以像这样使用它了:

    dialog.setVisible(true);
    frame.setEnabled(false);
    // Logic
    dialog.setVisible(false);
    frame.setEnabled(true);
    

    【讨论】:

    • 你到哪里去了,我的英雄?我用多线程、透明的 jframes 和 glasspanels 浪费了一整天的时间,你用 2 行代码解决了它
    【解决方案4】:

    从技术上讲,没有。就像 MadProgrammer 在评论中写的那样,您永远不会在 EDT 之外访问任何 Swing 组件,包括 JDialogs,因此您在问题中暗示的情况永远不会发生(除了 EDT 之外,永远不会有任何线程设置对话框可见)。

    不过,你可以让它看起来像。这就是 SwingUtilities.invokeLater(Runnable) 的用途 (doc)。

    import java.awt.Color;
    import java.awt.FlowLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.BorderFactory;
    import javax.swing.JDialog;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JProgressBar;
    import javax.swing.SwingUtilities;
    import javax.swing.Timer;
    
    public class BlockingDialogDemo extends JFrame {
    
        private Timer timer;
        private JDialog blocker;
    
        public BlockingDialogDemo() {
            setTitle("Blocking Dialog");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setSize(300, 200);
            setLocationRelativeTo(null);
    
            blocker = new JDialog(this, true);
            blocker.setLayout(new FlowLayout());
            blocker.setUndecorated(true);
            blocker.getRootPane().setBorder(BorderFactory.createMatteBorder(1, 1, 1, 1, Color.black));
            blocker.add(new JLabel("I'm blocking EDT!"));    
            JProgressBar progress = new JProgressBar();
            progress.setIndeterminate(true);
            blocker.add(progress);
            blocker.pack();
    
            timer = new Timer(3000, new ActionListener() {
    
                public void actionPerformed(ActionEvent e) {
                    doSomeWork();
                }
            });
            timer.setRepeats(false);
            timer.start();
        }
    
        private void doSomeWork() {
            // this executes on-EDT
            Runnable runnable = new Runnable() {
    
                public void run() {
                    // this executes off-EDT - never ever access Swing components here
                    showBlocker();
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ex) {
                        System.out.println("Ummm.. I was sleeping here!");
                    } finally {                
                        hideBlocker();
                    }
                }
            };
            new Thread(runnable).start();
        }
    
        private void showBlocker() {
            // this executes off-EDT                
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    // this executes on-EDT
                    blocker.setLocationRelativeTo(BlockingDialogDemo.this);
                    blocker.setVisible(true);
                }
            });
        }
    
        private void hideBlocker() {
            // this executes off-EDT
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    // this executes on-EDT
                    blocker.setVisible(false);
                    timer.restart();
                }
            });
        }
    
        public static void main(String[] args) {
            // this is called off-EDT
            SwingUtilities.invokeLater(new Runnable() {
    
                public void run() {
                    // this is called on-EDT
                    new BlockingDialogDemo().setVisible(true);
                }
            });
        }
    
    }
    

    【讨论】:

      【解决方案5】:

      这对我有用……有时:

      public class NonBlockingModalDialogDemo extends JFrame{
      
          JButton btnDoIt;
      
          public NonBlockingModalDialogDemo() {
              setTitle("NonBlockingModalDialog Demo");
              setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
              setSize(300,300);
              setLayout(new FlowLayout());
      
              btnDoIt = new JButton("Non-Blocking Notify");
              btnDoIt.addActionListener( new ActionListener(){
                  @Override
                  public void actionPerformed(ActionEvent arg0) {
                      JDialog asyncDialog = createNonBlockingModalDialog("Please wait while we do some work", "Please wait");
      
                      doWork(50);
                      //Once your done, just dispose the dialog to allow access to GUI
                      asyncDialog.dispose();
                  }
      
              });
      
              this.add(btnDoIt);
          }
      
          private JDialog createNonBlockingModalDialog(String message, String title)
          {
              final JDialog dialog = new JDialog();
              dialog.setLayout(new FlowLayout());
              dialog.add(new JLabel(message));
              dialog.setTitle(title);
              dialog.setModal(true);
              dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
              dialog.setAlwaysOnTop(true);
              dialog.pack();
      
              Runnable dialogDisplayThread = new Runnable() {
                  public void run() {
                      dialog.setVisible(true);
                  }};
      
              new Thread(dialogDisplayThread).start();
      
              //Need to wait until dialog is fully visible and then paint it
              //or else it doesn't show up right
              while(!dialog.isVisible()){/*Busy wait*/}
              dialog.paint(dialog.getGraphics());
      
              return dialog;
          }
      
          private void doWork(int amount) {
              for(int i = 0; i < amount; i++)
              {
                  System.out.println("Doing work step number " + i);
                  try {
                      Thread.sleep(100);
                  } catch (InterruptedException e) {}
              }
      
              System.out.println("done");
          }
      
          public static void main(String[] args) {
              new NonBlockingModalDialogDemo().setVisible(true);
          }
      }
      

      我真的不喜欢它有一个忙碌的等待来检查对话框是否可见,但到目前为止我还没有找到解决它的方法。无论如何,忙碌的等待应该不会花很长时间,所以这真的不重要。

      编辑: 我做了一些与此非常相似的事情,出于某种原因,在某些机器上,有时它会永远阻塞,甚至不显示对话框。

      我还没有找出根本原因,但这让我得出结论,所有说“永远不要在事件调度线程之外修改 GUI”的人可能是在做某事。

      也许与其尝试继续您需要在 EDT 上完成的工作,不如尝试以下方法: https://stackoverflow.com/a/4413563/2423283 它使用 SwingWorker 生成一个新线程,然后允许您在完成后更新 GUI 组件。

      【讨论】:

      • 永远不要从另一个线程调用 swing 元素。这就是您正在做的事情,它会导致意外行为
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多