Why are the Window/Component Listeners invoked differently when setVisible(false) and dispose() are called?
Asked Answered
A

2

7

The difference I see is (running on JDK 1.7):

setVisible(false), invokes componentHidden but not windowClosed (The API states only on dispose() so it's OK even if it irritates me)

but

dispose(), invokes windowClosed but not componentHidden

Short running example code (MCVE):

public class JDialogTest extends JDialog {
    private static final long serialVersionUID = 1L;

    public JDialogTest(JFrame owner){
        super(owner,ModalityType.APPLICATION_MODAL);
        init();
    }
    private void init() {
        this.getContentPane().setLayout(new GridLayout(1,2));
        JButton btnVisible = new JButton("Set visible false");
        btnVisible.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JDialogTest.this.setVisible(false);
            }
        });
        JButton btnDispose = new JButton("Dispose");
        btnDispose.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JDialogTest.this.dispose();
            }
        });
        this.getContentPane().add(btnVisible);
        this.getContentPane().add(btnDispose);
        this.pack();
    }

    public static void main(String[] args) {

        //A fake frame to test JDialog
        JFrame fakeFrame = new JFrame("Fake Frame");
        fakeFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        fakeFrame.getContentPane().setLayout(new BorderLayout());
        JButton btnOpen = new JButton("Open Dialog");
        fakeFrame.getContentPane().add(btnOpen,BorderLayout.CENTER);
        fakeFrame.pack();
        fakeFrame.setLocationRelativeTo(null);

        //Generate the test dialog
        final JDialogTest dialog = new JDialogTest(fakeFrame);
        dialog.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentShown(ComponentEvent e) {
                System.out.println("Component Shown");
            }
            @Override
            public void componentHidden(ComponentEvent e) {
                System.out.println("Component Hidden");
            }
        });

        dialog.addWindowListener(new WindowAdapter() {
            @Override
            public void windowOpened(WindowEvent e) {
                System.out.println("Window open");
            }
            @Override
            public void windowClosed(WindowEvent e) {
                System.out.println("Window closed");
            }
        });
        dialog.setLocationRelativeTo(null);

        btnOpen.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dialog.setVisible(true);
            }
        });
        fakeFrame.setVisible(true);
    }
}

NOTE: The example features a JDialog, but I see same behavior in JFrame, to test simple attach the listeners to the fakeFrame, and add similar buttons. (avoided in MVCE to keep it Minimal)).

I have considered this post:

JDialog setVisible(false) vs dispose()

  • In answers seems that is should be no difference, use dispose()...

API DOCUMENTATION:

Window.setVisible(boolean b), Window.dispose(), ComponentListener.componentHidden(ComponentEvent e), WindowListener.windowClosed(WindowEvent e)

Why do I care: Naturally out of curiosity, but also since I use buttons to close the window (invoking dispose()) and the interface can also be closed by the top/right window close icon and alt+F4 (invoking setVisible(false)!?). Hence no one of the above listener can be used. Only the HierarchyListener will catch them both and this seems counter intuitive.

EDIT: The question is scoped as "why is it like this"? What is the purpose?". I was expecting dispose() to invoke both! I can't find any clues in the API documentation for why not.

Adynamia answered 12/1, 2016 at 12:25 Comment(2)
Because they are different methods and this is what the source code shows it does. What did you expect? What type of answer are you looking for?Embargo
I have update the post to reflect what I was expecting and what answer I was hopping to get.Adynamia
E
4

the interface can also be close by top/right window close icon alt+F4 (invoking setVisible(false)!?)

This is determined by the default close operation. You can set it with setDefaultCloseOperation. The default is HIDE_ON_CLOSE, which is why you get a componentHidden invocation. If you set it to DISPOSE_ON_CLOSE then you will get a windowClosed invocation instead. Setting to the latter will allow you to only register for those event types.

Regardless, hiding and disposing do different things. Disposing releases the resources used while hiding doesn't. Also, hiding the last window will not exit the JVM while disposing it will.

As for the technical side of event dispatching, there are a lot of intricacies. While disposing the window does call its hiding method, the dispatching of events is done after operations are done. This means that the EDT can dispatch events "after the fact". Since the window is closed, it does not dispatch the hiding event.

Embargo answered 12/1, 2016 at 17:37 Comment(7)
Thanks for your answer, yes I was ignoring the DISPOSE_ON_CLOSE on the JDialog would change the behavior, so naturally if I use button with dispose I need to set this as default close operation. This settles my frustration.. but our dear oracle developers could have launched a nice componentHidden before their "intricacies"Adynamia
@PetterFriberg You're welcome. I don't think that disposing a window should trigger a componentHidden because a disposed window is not hidden, it doesn't exist so to speak. Hidden implies that it does exists, only not shown.Embargo
Also as an example, just like not every call to hide dispatches a componentHidden event so does not every call to requestFocusInWindow dispatches a focusGained event, as noted in the API: "Developers must never assume that this Component is the focus owner until this Component receives a FOCUS_GAINED event.".Embargo
I will not argue, it was my mind that the function dispose worked something like this public void dispose(){setVisible(false);.....dispose the stuff}, thanks again...Adynamia
Well lets argue a bit " it doesn't exist so to speak", just set visible(true) and you see it again componentShowed.... note I will delete this comment if you don't wish to argueAdynamia
@PetterFriberg That's the point, dispose() does call hide() (same as setVisible(false)). But the events are only added to the queue at this point. They are dispatched later, and since the closed event occurred, hidden is not dispatched.Embargo
@PetterFriberg And about showing a disposed window, the API does state "The Window and its subcomponents can be made displayable again by rebuilding the native resources with a subsequent call to pack or show.", but it will rebuild the window again, reallocating the memory and resources. So if there is not enough memory to rebuild the window, your call to setVisible(true) will not generate a shown event after disposing, but it will after just hiding because the memory was not released.Embargo
P
1

I came accross this question while struggling with the behaviour described. My handler for componentHidden were not called although a ComponentEvent(COMPONENT_HIDDEN) was scheduled to the dialog's event queue by setVisible(false).

My dialog is modal and the caller invokes dispose explicitely after the dialog is closed and returns from setVisible(true). Probably this is a "race condition" between the dialog's event queue und the application's event queue. My work around therefore is on the caller's side:

SwingUtilities.invokeLater(myDialog::dispose);

This should postpone the disposal of the dialog until its event queue is exhausted; componentHidden is called in this setting.

Proof answered 21/12, 2020 at 14:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.