Listen for when a Component is Shown for the First Time

前端 未结 6 1524
日久生厌
日久生厌 2021-01-07 22:29

I am trying to capture the very first moment when a component is shown on the screen without using \'dirty\' solutions as with use of a timer. Basically, I want to know the

相关标签:
6条回答
  • 2021-01-07 22:56

    I"ve use an AncestorListener and handled the ancestorAdded event.

    0 讨论(0)
  • 2021-01-07 22:56

    For most purposes you can go with the first call to ComponentListener.componentMoved (or if you are also interested in the size ComponentListener.componentResized). Those are called whenever the position/size of the component changes.

    0 讨论(0)
  • 2021-01-07 22:59

    Oddly, the ComponentListener works just fine when applied to the JFrame. Here is the altered source where I saw it work.

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class CompListenerTest
    {
        static ComponentListener cL = new ComponentAdapter()
        {
            @Override
            public void componentShown(ComponentEvent e)
            {
                super.componentShown(e);
                System.out.println("componentShown");
            }
        };
    
        public static void main(String[] args)
        {
            JPanel p = new JPanel();
            p.setPreferredSize(new Dimension(300, 400));
            p.setBackground(Color.GREEN);
    
            System.out.println("initial test p="+p.isShowing());
            JPanel contentPane = new JPanel();
            contentPane.setBackground(Color.RED);
            contentPane.add(p);
            JFrame f = new JFrame();
            f.addComponentListener(cL);
            f.setContentPane(contentPane);
            f.setSize(800, 600);
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            f.setVisible(true);
        }
    }
    
    0 讨论(0)
  • 2021-01-07 23:00

    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

    0 讨论(0)
  • 2021-01-07 23:07

    Using AncestorListener and ancestorAdded worked for me. Here's sample code:

    addAncestorListener(new AncestorListener() {
        @Override
        public void ancestorRemoved(AncestorEvent event) {}
    
        @Override
        public void ancestorMoved(AncestorEvent event) {}
    
        @Override
        public void ancestorAdded(AncestorEvent event) {
            // component is shown here
        }
    });
    
    0 讨论(0)
  • 2021-01-07 23:14
    static ComponentListener cL = new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            super.componentResized(e);
            System.out.println("componentShown = "+e.getComponent().isDisplayable());
        }
    };
    
    0 讨论(0)
提交回复
热议问题