[英]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();
}
}
}
可能有用的信息:
-> 私人布爾處置 = 假;
然后在調用 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.