user21760
user21760

Reputation: 93

getPreferredSize() only called twice after pack()

Why does getPreferredSize() get called only twice when pack() is called on a JFrame, like in the following example:

public class PackTest {
    static JFrame f = new JFrame();
    @SuppressWarnings("serial")
    public static void main(String[] args) {
        f.add(new JPanel() {
            int i = 0;
            @Override
            public Dimension getPreferredSize() {
                System.out.println("getPreferredSize() called");
                if(i++ >= 2)
                    return new Dimension(200, 200); // This is never returned
                else
                    return new Dimension(100, 100);
            }
        });
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.pack();
        f.setVisible(true);
        for(int i = 0; i < 10; i++) {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch(InterruptedException e) {}
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    System.out.println("Calling pack()");
                    f.pack();
                }
            });
        }
    }
}

It seems that getPreferredSize() only gets called as long as it keeps on returning something different (i.e. if it returns a different dimension every time, it will get called 10 times in the example). Why does Swing do this, and how do I get pack() to resize the JFrame properly?

Upvotes: 2

Views: 509

Answers (2)

Vishal K
Vishal K

Reputation: 13066

getPreferredSize() is always called when the parent container resizes or revalidates . Whatever Dimension value returned by this method is ,it enforces a fixed size on a component(if overridden) that shouldn't change regardless of the size of the window and the other components.

Upvotes: 2

Hovercraft Full Of Eels
Hovercraft Full Of Eels

Reputation: 285405

Try revalidating your JPanel to get it to call it's preferredSize:

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

public class PackTest {

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }

   public static void createAndShowGui() {
      final JFrame f = new JFrame();
      final JPanel panel = new JPanel() {
         int i = 0;

         @Override
         public Dimension getPreferredSize() {
            System.out.println("getPreferredSize() called");
            if (i++ >= 2)
               return new Dimension(200, 200); // This is never returned
            else
               return new Dimension(100, 100);
         }
      };
      f.add(panel);
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.pack();
      f.setVisible(true);

      int delay = 1000;
      new Timer(delay, new ActionListener() {

         @Override
         public void actionPerformed(ActionEvent arg0) {
            System.out.println("Calling pack()");
            panel.revalidate();
            f.pack();
         }
      }).start();
   }
}

As an aside, you'll want to make sure that with your real code you take pains to respect Swing threading rules.

Upvotes: 3

Related Questions