Listen for when a Component is Shown for the First Time
The reason a ComponentListener doesn't work is that it reports changes to the visible property - and that is true by default, even without being part of the component hierarchy.
To be reliably notified, use a HierarchyListener
Edit (musings about my knowledge evolution in regard to this question/answers, not sure what the netiquette has to say about doing it ... simply guide me if that's the wrong way to go :-)
First: the question as asked in the subject is not necessarily related to the actual problem (as commented by Boro below - any way to link to a comment?): there's no need to keep some kind of local flag to decide whether or not it is safe to send a getLocationOnScreen to a component, simply ask the component itself. Learn-item 1 for myself :-)
Second: The question as asked is quite interesting. Five experts (including myself, self-proclaimed), five different answers. Which triggered a bit of digging on my part.
My hypothesis: ComponentEvents are not useful for notification of (first-)showing. I knew that componentShown is useless because it's a kind-of propertyChange notification of the visible property of a component (which rarely changes). Puzzled about the suggested usefulness of moved/resized, though.
Constructing a use-case: fully prepare the frame in the example and keep it ready for later showing, a typical approach to improve perceived performance. My prediction - based on my hypothesis: resized/moved fired at prepare-time, nothing at show-time (note: the isShowing is what we are after, that is the latter). A snippet to add in the OP's example:
final JFrame f = new JFrame();
f.setContentPane(contentPane);
f.setSize(800, 600);
// f.pack();
JFrame controller = new JFrame("opener");
controller.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Action open = new AbstractAction("open/hide second") {
@Override
public void actionPerformed(ActionEvent e) {
f.setVisible(!f.isVisible());
}
};
controller.add(new JButton(open));
controller.pack();
controller.setVisible(true);
Disappointment: no notification at prepare-time, notification at show-time, just as needed, my hypothesis seemed wrong ;-) Last chance: swap the setSize for a pack ... and voila, notification at prepare-time, no notification at show-time, happy me again. Playing a bit more: looks like ComponentEvents are fired if the a component is displayable, which may or may not be useful in some contexts but not if showing is the state we are after. The
New imperial rules (draft):
Do not use ComponentListener for notification of "showing". That's left-over from AWT-age.
Do use AncestorListener. That seems to be the Swing replacement, slightly misnomed notification of "added" which actually means "showing"
Do use HierarchyListener only if really interested in fine-grained state changes
I"ve use an AncestorListener and handled the ancestorAdded event.