簡體   English   中英

Java - 多次調用自定義 JDialog 處理方法 - 它不打算這樣做

[英]Java - Custom JDialog dispose method called more than once - it is not intended to do so

我正在編寫一個腳本,它涉及 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();
        }
    }
}

如果我運行應用程序然后打開和關閉/處理對話框,問題就很明顯。 通過將Exception e放置在BaseDialog的“dispose”方法下,我在通過窗口的“X”按鈕關閉時得到以下輸出(省略內部調用):

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. 有點不相關,但“黑客”是在 BaseDialog 類下創建一個私有字段:

-> 私人布爾處置 = 假;

然后在調用 dispose 方法時進行檢查:

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

雖然它可能會解決問題,但它遠不是一個好的答案,並且由於核心沒有正確固定,未來很容易出現問題。

父窗口持有對對話框的引用,並會在處理對話框本身時重新處理對話框。

您的問題是您沒有為對話框分配父窗口,因此出於某種原因,在處理對話框時,它會重新分配以前所做的所有對話框引用,就好像基類保存對子窗口的引用一樣。 您可以通過將父窗口傳遞到對話框的超級構造函數來解決該問題,如下所示:

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 保存對所有創建的對話框的引用,無論它們是否已被釋放,並在關閉時重新釋放它們。

有點晚了,但我仍然認為這對某人有幫助。

我遇到了同樣的問題,並檢查了我為沒有參數的構造函數發現的JDialog文檔文檔說

注意:此構造函數不允許您創建無主 JDialog。 要創建無主 JDialog,您必須使用參數為 null 的 JDialog(Window) 或 JDialog(Dialog) 構造函數。

所以我只是嘗試使用JDialog(Window)JDialog(Dialog)並且它起作用了!

我只需要寫這段代碼

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

如果您無法訪問應該是父級的 JFrame(這是我的情況),這可能會有所幫助

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM