I have an application with an abstract class that extends JDialog
. The class as an abstract void onClose()
, and, in the class's constructor, the following code is added:
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
onClose();
}
}
The event is fired when expected, but then a strange thing happens. When a concrete extension of this class has code to create a new JDialog
in the onClose()
method, and this JDialog
's defaultCloseOperation
is JDialog.DISPOSE_ON_CLOSE
, the event is fired continuously, until I force quit the operation.
I have isolated the code to the following SSCCE:
// package removed
// imports removed
public class SSCCE extends JDialog {
public static void main(String[] args) {
SSCCE s = new SSCCE();
s.pack();
s.setVisible(true);
}
public SSCCE() {
setLayout(new GridLayout(1, 0, 0, 0));
JButton btn = new JButton("click me");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
dispose();
}
});
addWindowListener(new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
System.out
.println("SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()");
onClose();
}
});
add(btn);
}
public void onClose() {
JDialog dialog = new JDialog();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
}
}
Upon clicking the "click me" button, the blank JDialog
appears and SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()
appears in the console window. When I close the blank dialog, it reappears again and the text appears again.
Another really interesting thing is that when I change the initialization line from
JDialog dialog = new JDialog();
to
JDialog dialog = new JDialog() {
@Override
public synchronized void addWindowListener(WindowListener l) {
super.addWindowListener(l);
System.out
.println("SSCCE.onClose().new JDialog() {...}.addWindowListener()");
}
};
I get the following output in the console:
When clicking the "click me" button:
SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
At the first closing of the dialog:
SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
At the second closing of the dialog:
SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
SSCCE.onClose().new JDialog() {...}.addWindowListener()
Each time I close the dialog, addWindowListener(WindowListener l)
is called an additional time, even though it I am not intentionally calling it.
I don't really want any WindowListener
s to be registered on the dialog, but I think that simply overriding the addWindowListener(...)
method and not calling super.addWindowListener(...)
would be too sloppy.
I'm running Java 1.6.0_31 on Mac OS X 10.6.8, using Eclipse Indigo (with WindowBuilder, if it matters).
Does anyone have any ideas?
Thanks!
As per the Java Dialog tutorial,
Every dialog is dependent on a Frame component. When that Frame is destroyed, so are its dependent Dialogs.
When you use the JDialog constructor without any arguments, it
Creates a modeless dialog without a title and without a specified Frame owner. A shared, hidden frame will be set as the owner of the dialog.
That shared hidden frame is SwingUtilities$SharedOwnerFrame
, and on initialization it registers a WindowListener
to all owned windows.
When you close your dialog, the SharedOwnerFrame
's windowClosed
method is called, which checks for all windows it owns (at this point is the original SSCCE dialog and the new one), it finds none are visible, and so it disposes itself. This has the impact of disposing all of the dialogs it owns, which posts a window close event to each. This calls windowClosed
in your listener, opening a new dialog. And round we go again :-). Regarding your last comments, you get additional log lines each time because you get one per dialog the SharedOwnerFrame
owns.
If you make the SSCCE dialog own the new dialog (by passing this
into its constructor) then you don't end up with shared ownership and it works fine:
public void onClose() {
JDialog dialog = new JDialog(this);
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
}
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JOptionPane;
public class SSCCE extends JDialog {
public static void main(String[] args) {
SSCCE s = new SSCCE();
s.pack();
s.setVisible(true);
}
private WindowAdapter wa = new WindowAdapter() {
@Override
public void windowClosed(WindowEvent e) {
System.out
.println("SSCCE.SSCCE().new WindowAdapter() {...}.windowClosed()");
onClose();
}
};
public SSCCE() {
setLayout(new GridLayout(1, 0, 0, 0));
JButton btn = new JButton("click me");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
dispose();
}
});
addWindowListener(
wa);
add(btn);
}
public void onClose() {
removeWindowListener(wa);
JDialog dialog = new JDialog();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With