【问题标题】:How to close a modal JDialog when user clicks outside of JDialog?当用户在 JDialog 之外单击时如何关闭模态 JDialog?
【发布时间】:2010-12-13 03:39:19
【问题描述】:

我有一个未装饰的模态 JDialog,当用户在模态对话框之外单击时,我想将其设置为 setVisible(false)。

这在 Swing 中可行吗?

我正在做的是为文本字段(如日期选择器)弹出一个自定义编辑器。有没有更简单的方法来做我想做的事?

编辑

请记住,调用 setVisible(true) 时模态会阻塞,因此您不能只说“不要使用模态对话框”

我已经尝试在对话框上聚焦监听器,但它们不会在其模态时触发。

【问题讨论】:

  • 您是否尝试过 addAWTEventListener 方法,这应该为您提供所有指定事件类型的事件,例如在我下面给出的示例中,这将是所有鼠标事件。
  • 我知道你说“所以你不能只说“不要使用模式对话框””,大概是因为你有在 setVisible 调用之后直接执行的代码?当对话框关闭时,你能不能把它移到一个监听器中?在不了解应用程序细节的情况下,它可能会提供更简洁的设计,尤其是在单元测试方面,我喜欢将对话框移到获取用户响应的策略中,这样我就可以注入模拟策略而无需在运行时挂起单元测试无头或无需以编程方式创建事件。

标签: java swing modal-dialog jdialog


【解决方案1】:

编辑: 更改为使用 WindowFocusListener 而不是 FocusListener,并检查焦点丢失时的下降组件,以便在子组件获得焦点时不隐藏。

一个简单的方法是在对话框上添加一个窗口焦点监听器,当焦点丢失时隐藏它。在这种情况下,我认为不需要模态。例如:

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class ClickAwayDialog extends JDialog {

    public ClickAwayDialog(final Frame owner) {
        super(owner);
        JPanel pnl = new JPanel(new BorderLayout());
        pnl.add(new JLabel("Click outside this dialog in the parent frame to close it"), BorderLayout.NORTH);
        JButton btn = new JButton("Click Me");
        btn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(ClickAwayDialog.this, "New Child Window");
            }
        });
        pnl.add(btn, BorderLayout.CENTER);
        this.setContentPane(pnl);
        this.pack();
        this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        this.setLocationRelativeTo(owner);
        this.setAlwaysOnTop(true);
        this.addWindowFocusListener(new WindowFocusListener() {

            public void windowGainedFocus(WindowEvent e) {
                //do nothing
            }

            public void windowLostFocus(WindowEvent e) {
                if (SwingUtilities.isDescendingFrom(e.getOppositeWindow(), ClickAwayDialog.this)) {
                    return;
                }
                ClickAwayDialog.this.setVisible(false);
            }

        });
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JFrame parent = new JFrame();
                parent.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
                parent.setSize(300, 300);
                parent.setLocationByPlatform(true);
                parent.setVisible(true);
                ClickAwayDialog dlg = new ClickAwayDialog(parent);
                dlg.setVisible(true);                
            }
        });
    }
}

【讨论】:

  • 失去焦点必须确保获得焦点的组件不是对话框的子组件,因此您需要通过getParent()向上搜索组件层次结构
  • @vickirk -- 好点。我更改了上面的示例以检查后代组件(可以说是边缘情况,但我增强了示例以允许创建对话框的子窗口)。我还将它更改为使用窗口焦点侦听器。
  • 是的,我完全理解如何在没有模式对话框的情况下做到这一点,但问题的关键是是否可以使用模式对话框来做到这一点。使用模态对话框的主要优点是它在 setVisible(true) 时会阻塞。我的目标是不需要重组程序以使用非模态对话框或编写实用程序来模拟非模态对话框中的阻塞性质。
【解决方案2】:

如果您可以在其外部单击并且“某事”发生,则它不是模式对话框。所有答案都是正确的,您应该创建一个非模态对话框,然后通过 FocusListener 处理您的用例。

【讨论】:

  • 这是最接近“否”的答案。这就是我要问的。如果没有非模态对话框,我无法做到这一点。
【解决方案3】:

不必是模态对话框(模态意味着它会阻止您使用所有者窗口,直到您隐藏对话框)。最好试试这个:

final JDialog dlg ...
dlg.setModal(false);

dlg.addWindowFocusListener(new WindowFocusListener() {            
    public void windowLostFocus(WindowEvent e) {
        dlg.setVisible(false);
    }            
    public void windowGainedFocus(WindowEvent e) {
    }
});

【讨论】:

    【解决方案4】:

    尝试将模态设置为false,然后使用windowsDeactivated()关闭对话框(dialog.dispose()),对我有用。

    【讨论】:

      【解决方案5】:

      使用 WindowListener 并处理 windowDeactivated() 事件。

      【讨论】:

      • 我刚刚对此进行了测试,对我来说,这种方法似乎只在完全点击 java 应用程序外部时才有效,但在尝试点击生成模式对话框的 java 主框架时无效。
      • @Zalumon,我想这个答案不是很清楚。这是一个不使用模态 JDialog 的建议。当您使用模态 JDialog 作为弹出窗口时,如果您在对话框外部单击,则无法关闭对话框。这是一种解决方案,允许您在单击对话框区域外时关闭non modal dialog,从而关闭“弹出对话框”。
      【解决方案6】:

      不是真正的模态对话框,然后如果单击其他位置关闭它,也许你想要setAlwaysOnTop

      但是,类似以下的内容应该可以解决问题(未经测试)。请注意,我建议将代码移动到比提供的使用更好的设计中。

      static JDialog dialog = ...
      
      Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
          public void eventDispatched(AWTEvent e) {
              dialog.setVisible(false);
      
              SwingUtils.invokeLater(new Runnable(){
                  public void run(){
                      Toolkit.getDefaultToolkit().removeAWTEventListener(this);
                  }
              });        
          }
      }, AWTEvent.MOUSE_EVENT_MASK);
      
      dialog.setVisible(true);
      

      【讨论】:

      • 我刚刚对此进行了测试,看起来这种监听器也没有从模态对话框外部获得任何鼠标事件。
      【解决方案7】:

      可能添加一个FocusListener 并在对话框失去焦点时隐藏它。如果对话框中的某些元素可以具有焦点,则可能会很棘手。无论如何,尝试一下。

      【讨论】:

      • 除了切换到另一个应用程序之外,模态对话框(和子组件)是否可以放松焦点?
      • 啊,明白你的意思,你的意思不是模态对话框!不知道为什么这被否决了
      猜你喜欢
      • 1970-01-01
      • 2012-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多