Reputation: 91
I am working on a script which involves JDialogs popping up consecutively to ask the user to capture information on the screen (such as boxes around a menu, etc) so the program would afterwards automatically click where I designate it to.
The problem I am currently stuck on is that when I close my custom JDialog, the dispose method is seemingly called multiple times, although I have no idea why.
Here is a complete working example of the issue:
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();
}
}
}
The problem is evident if I run the application and then open and close/dispose the dialog. With the placement of the Exception e under BaseDialog's "dispose" method I get the following output upon closing through the window's 'X' button (omitting internal calls):
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)
Also notice that if you run the application and 'open - close' the dialog repeatedly, the method calls increment by 1 (best visible if you comment out the first two lines under the overridden 'dispose' method regarding the exception stacktrace printing and watch the output).
Here is a hard-stripped version if you would want to use/look at and thank you in advance above all!
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();
}
}
}
Possibly helpful information:
-> private boolean disposed = false;
And then make a check when calling the dispose method:
@Override
public void dispose() {
if (!disposed) {
disposed = true;
super.dispose();
}
}
While it MAY solve the issue, it is far from a good answer and future problems may easily exist since the core is not fixed properly.
Upvotes: 2
Views: 2153
Reputation: 101
A bit late, but still I think it'd be helpful to someone.
I had the same problem and checking the docs for the JDialog
I discovered for the constructor without parameters documentation says
NOTE: This constructor does not allow you to create an unowned JDialog. To create an unowned JDialog you must use either the JDialog(Window) or JDialog(Dialog) constructor with an argument of null.
So I simply tried using the JDialog(Window)
or JDialog(Dialog)
and it worked!
I just had to write this code
public class MyDialog extends JDialog {
public MyDialog() {
super((Window)null);
}
}
It might be helpful if you can't access the JFrame that should be the parent (which was my case)
Upvotes: 0
Reputation: 285405
The parent window holds a reference to the dialog and will re-dispose of the dialog when it itself is disposed.
Your problem is that you're not assigning a parent window for the dialog, and so for some reason when the dialog is disposed it re-dosposes all dialog references previously made, as if the base class holds the references to the child window. You can solve the issue by passing the parent window into the dialog's super constructor, like so:
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);
}
}
}
Myself, I avoid re-creating the dialog if I think that I might need it, which would prevent unnecessary accumulation of references. See code below and comment and uncomment the section indicated to see what I mean:
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);
}
}
Again, note that the parent JFrame holds references to all dialogs created, whether they've been disposed or not, and re-disposes them all on closing.
Upvotes: 1