Java - 不止一次调用自定义JDialog dispose方法 - 它不打算这样做

时间:2016-07-22 02:03:02

标签: java dispose jdialog

我正在编写一个脚本,其中涉及JDialogs连续弹出以要求用户在屏幕上捕获信息(例如菜单周围的框等),以便程序随后自动点击我指定的位置。

我目前遇到的问题是,当我关闭自定义JDialog时,dispose方法似乎被多次调用,虽然我不知道为什么。

以下是该问题的完整工作示例:

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

public class Main {

    // Creates the main frame (MainForm extends JFrame)
    private static MainForm mainForm = new MainForm();

    public static void main(String[] args) {
        mainForm.setVisible(true);
    }

    static class BaseDialog extends JDialog {
        BaseDialog() {
            super();
            setModal(true);
        }

        // Overrides and calls (super)dispose method of JDialog - Nothing unusual
        @Override
        public void dispose() {
            Exception e = new Exception();
            e.printStackTrace();
            System.out.println("disposing");
            super.dispose();
        }
    }

    static class CaptureDialog extends BaseDialog implements ActionListener {
        CaptureDialog() {
            super();
            JButton btnInventory = new JButton("Close Me");
            btnInventory.addActionListener(this);
            add(btnInventory);
            setTitle("Recapture");
            setModalityType(ModalityType.APPLICATION_MODAL);
            setLocationRelativeTo(null);
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
            setResizable(false);
            setSize(200, 80);
            setVisible(true);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Clicked the button!");
            dispose();
        }
    }

    static class MainForm extends JFrame implements ActionListener {
        MainForm() {
            super("Example");
            JButton btnCapture = new JButton();
            btnCapture.setText("Capture");
            btnCapture.addActionListener(this);
            add(btnCapture);
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            setLocationRelativeTo(null);
            setSize(200, 80);
        }

        // Only one button is added to action listener ('if' not necessary)
        @Override
        public void actionPerformed(ActionEvent e){
            new CaptureDialog();
        }
    }
}

如果我运行应用程序然后打开并关闭/配置对话框,问题就很明显了。通过在 BaseDialog 的“dispose”方法下放置例外e ,我在关闭窗口的“X”按钮时会得到以下输出(省略内部调用):< / p>

java.lang.Exception
    at Main$BaseDialog.dispose(Main.java:24)
    at javax.swing.JDialog.processWindowEvent(JDialog.java:691)
    at java.awt.Window.processEvent(Window.java:2017)
    at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:184)
    at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:229)
    at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:227)
    at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:227)
    at java.awt.Dialog.show(Dialog.java:1084)
    at java.awt.Component.show(Component.java:1673)
    at java.awt.Component.setVisible(Component.java:1625)
    at java.awt.Window.setVisible(Window.java:1014)
    at java.awt.Dialog.setVisible(Dialog.java:1005)
    at Main$CaptureDialog.<init>(Main.java:46)
    at Main$MainForm.actionPerformed(Main.java:71)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
disposing
disposing
java.lang.Exception
    at Main$BaseDialog.dispose(Main.java:24)
    at java.awt.Window.disposeImpl(Window.java:1161)
    at java.awt.Window$1DisposeAction.run(Window.java:1189)
    at java.awt.Window.doDispose(Window.java:1210)
    at java.awt.Window.dispose(Window.java:1151)
    at javax.swing.SwingUtilities$SharedOwnerFrame.dispose(SwingUtilities.java:1814)
    at javax.swing.SwingUtilities$SharedOwnerFrame.windowClosed(SwingUtilities.java:1792)
    at java.awt.Window.processWindowEvent(Window.java:2061)
    at javax.swing.JDialog.processWindowEvent(JDialog.java:683)
    at java.awt.Window.processEvent(Window.java:2017)

另请注意,如果您运行应用程序并重复“打开 - 关闭”对话框,则方法调用递增1(如果您在覆盖'dispose'方法下注释掉前两行关于异常堆栈跟踪打印,则最佳可见并观看输出。)

如果您想使用/查看并且事先感谢您,这是一个硬剥离的版本!

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

public class Main {
    public static void main(String[] args) {
        new MainForm();
    }

    static class BaseDialog extends JDialog {
        BaseDialog() {
            super();
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
            setVisible(true);
        }

        @Override
        public void dispose() {
            new Exception().printStackTrace();
            System.out.println("disposing");
            super.dispose();
        }
    }

    static class MainForm extends JFrame implements ActionListener {
        MainForm() {
            super();
            JButton btnCapture = new JButton();
            btnCapture.setText("Capture");
            btnCapture.addActionListener(this);
            add(btnCapture);
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            setSize(200, 80);
            setVisible(true);
        }

        @Override
        public void actionPerformed(ActionEvent e){
            new BaseDialog();
        }
    }
}

可能有用的信息:

  1. 省略'super.dispose()'导致'dispose'只被调用一次 - 应用程序代码应该不是问题。
  2. 如果我扩展JFrame而不是JDialog(关于BaseDialog类),则可以正确调用dispose方法(使用super.dispose())。
  3. 无关紧要,但'hack'是在BaseDialog类下创建一个私有字段:
  4. - &GT; private boolean dispos = false;

    然后在调用dispose方法时进行检查:

    @Override
    public void dispose() {
        if (!disposed) {
            disposed = true;
            super.dispose();
        }
    }
    

    虽然它可以解决问题,但它远非一个好的答案,未来的问题可能很容易存在,因为核心没有得到妥善修复。

2 个答案:

答案 0 :(得分:1)

父窗口包含对话框的引用,并在对话本身处理时重新处理对话框。

您的问题是您没有为对话框分配父窗口,因此出于某种原因,当对话框被丢弃时,它会重新处理以前创建的所有对话框引用,就像基类保存对象的引用一样。孩子的窗户。您可以通过将父窗口传递到对话框的超级构造函数来解决问题,如下所示:

import javax.swing.*;

import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Main {

    // Creates the main frame (MainForm extends JFrame)
    private static MainForm mainForm = new MainForm();

    public static void main(String[] args) {
        mainForm.setVisible(true);
    }

    static class BaseDialog extends JDialog {
        BaseDialog(Window win) {
            super(win);
            setModal(true);
        }

        // Overrides and calls (super)dispose method of JDialog - Nothing
        // unusual
        @Override
        public void dispose() {
            Exception e = new Exception();
            // e.printStackTrace();
            String text = String.format("Disposing. This hashCode: %08X", hashCode());
            System.out.println(text);
            super.dispose();
        }
    }

    static class CaptureDialog extends BaseDialog implements ActionListener {
        CaptureDialog(Window win) {
            super(win);
            JButton btnInventory = new JButton("Close Me");
            btnInventory.addActionListener(this);
            add(btnInventory);
            setTitle("Recapture");
            setModalityType(ModalityType.APPLICATION_MODAL);
            setLocationRelativeTo(null);
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
            setResizable(false);
            setSize(200, 80);
            setVisible(true);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Clicked the button!");
            dispose();
        }
    }

    static class MainForm extends JFrame implements ActionListener {
        MainForm() {
            super("Example");
            JButton btnCapture = new JButton();
            btnCapture.setText("Capture");
            btnCapture.addActionListener(this);
            add(btnCapture);
            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            setLocationRelativeTo(null);
            setSize(200, 80);
        }

        // Only one button is added to action listener ('if' not necessary)
        @Override
        public void actionPerformed(ActionEvent e) {
            new CaptureDialog(MainForm.this);
        }
    }
}

我自己,如果我认为我可能需要它,我会避免重新创建对话框,这样可以防止不必要的引用累积。请参阅下面的代码并注释并取消注释显示的部分以了解我的意思:

import java.awt.Window;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class MainSimple extends JPanel {
    private JDialog dialog;

    public MainSimple() {
        add(new JButton(new OpenDialogAction("Open Dialog", KeyEvent.VK_O)));
        add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
    }

    private class OpenDialogAction extends AbstractAction {
        public OpenDialogAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            boolean test = true;
            // test = (dialog == null); // ***** comment or uncomment this line *****
            if (test) {
                Window win = SwingUtilities.getWindowAncestor(MainSimple.this);
                dialog = new MyDialog(win);
                dialog.pack();
                dialog.setLocationRelativeTo(win);
            }
            dialog.setVisible(true);            
        }
    }

    private class MyDialog extends JDialog {

        public MyDialog(Window win) {
            super(win, "My Dialog", ModalityType.APPLICATION_MODAL);
            add(new JButton(new DisposeAction("Close", KeyEvent.VK_C)));
            setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        }

        @Override
        public void dispose() {
            String text = String.format("Disposing. This hashCode: %08X", hashCode());
            System.out.println(text);
            super.dispose();
        }
    }

    private class DisposeAction extends AbstractAction {

        public DisposeAction(String name, int mnemonic) {
            super(name);
            putValue(MNEMONIC_KEY, mnemonic);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            Component source = (Component) e.getSource();
            Window win = SwingUtilities.getWindowAncestor(source);
            win.dispose();
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }

    private static void createAndShowGui() {
        MainSimple mainPanel = new MainSimple();
        JFrame frame = new JFrame("Main");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(mainPanel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

}

再次注意,父JFrame保存对创建的所有对话框的引用,无论它们是否已被处置,并在关闭时重新处理它们。

答案 1 :(得分:0)

有点晚了,但我仍然认为它会对某人有所帮助。

我遇到了同样的问题,并检查了我为没有参数的构造函数发现的 JDialog 文档说

<块引用>

注意:此构造函数不允许您创建无主 JDialog。要创建无主 JDialog,您必须使用参数为 null 的 JDialog(Window) 或 JDialog(Dialog) 构造函数。

所以我只是尝试使用 JDialog(Window)JDialog(Dialog) 并且成功了!

我只需要写这段代码

public class MyDialog extends JDialog {
    public MyDialog() {
        super((Window)null);
    }
}

如果您无法访问应该是父级的 JFrame(这是我的情况),这可能会有所帮助