javaswingresizecomponentlistener

When during initialization can I add a ComponentListener such that it won't fire componentResized?


The answer, I would think, is after all initial component sizing is done, but take a look at this SSCCE:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class Test2 {

  private static void createAndShowGUI2() {

    final JFrame frame = new JFrame("Test2");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    JButton addSecondListener = new JButton("Click me to add a second listener");
    addSecondListener.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        frame.addComponentListener(new ComponentAdapter() {
          public void componentResized(ComponentEvent e) {
            System.out.println("Component Listener 2");
          }
        });
      }
    });

    frame.getContentPane().add(addSecondListener);

    frame.setSize(200, 200);
    frame.setVisible(true);

    frame.addComponentListener(new ComponentAdapter() {
      public void componentResized(ComponentEvent e) {
        System.out.println("Component Listener 1");
        //throw new NullPointerException("Just getting traceback...");
      }
    });

  }

  public static void main(String[] args) {
    //Schedule a job for the event-dispatching thread:
    //creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        createAndShowGUI2();
      }
    });
  }
}

Even though Component Listener 1 is added after sizing the frame and making it visible, a resize event is called when you start up java Test2; it will print Component Listener 1 first thing.

This initially lead me to believe that adding a component listener will always cause a resize event to fire right after. However, this is not the case. If you press the JButton inside the frame component listener 2 will be added, and you will not see its print Component Listener 2 right as you do, instead it will only fire when you resize the frame.

What is causing component listener to fire after the frame has been sized before even being set visible? I threw an exception there to see what the traceback (currently commented out) to give me some hints:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Just getting traceback...
    at Test2$2.componentResized(Test2.java:31)
    at java.awt.Component.processComponentEvent(Component.java:5960)
    at java.awt.Component.processEvent(Component.java:5914)
    at java.awt.Container.processEvent(Container.java:2023)
    at java.awt.Window.processEvent(Window.java:1816)
    at java.awt.Component.dispatchEventImpl(Component.java:4501)
    at java.awt.Container.dispatchEventImpl(Container.java:2081)
    at java.awt.Window.dispatchEventImpl(Window.java:2458)
    at java.awt.Component.dispatchEvent(Component.java:4331)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

For my application I want to add a ComponentListener after all initial sizing is programmatically set without it firing componentResized (until the user actually changes the size of the frame/component). How can I do that?

Edit: I know there are ways around this, such as adding a boolean that is initially false and set afterwards, to ignore the first resize event. However that doesn't appear to me as super robust, and I just want to know what is going on.


Solution

  • You could add the listener inside SwingUtilities.invokeLater() to make sure the frame resizes first before adding the listener (I don't think this would be considered a workaround):

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            frame.addComponentListener(new ComponentAdapter() {
                public void componentResized(ComponentEvent e) {
                    System.out.println("Component Listener 1");
                }
            });
        }
    });