Search code examples
javaswingjframejpanelawt

How do I dynamically resize a JFrame based on its components (a JPanel)?


Basically my GUI is like this:

A frame that contains a free designed layout JPanel called mainPanel

Inside mainPanel are two other panels:

A) toolPanel of layout BoxLayout

B) gamePanelof layout GridLayout

These are the significant bits of the code:

public Normalv3() {
    initComponents();
    importBombs();
}

public void importBombs() {

    rows = Minesweeper.rows;
    columns = Minesweeper.columns;
    total = rows*columns;
    bombsIndexes = new ArrayList<Integer>(Minesweeper.totalBombs);

    // Creating a new grid layout and putting
    // it inside the old layout
    gamePanel.setLayout(new java.awt.GridLayout(rows, columns));


    ////
    Random ran = new Random();
    int low = 1;
    int high = rows*columns;
    int ranValue;
    for (int i = 0; i< Minesweeper.totalBombs; i++) {
        ranValue = ran.nextInt(high - low) + low;

        if(bombsIndexes.contains(ranValue)) {
            i--;
            continue;
        }

        bombsIndexes.add(ranValue);
    }
    ////

    for (int i = 1; i <= total; i++) {
        btnMines b = new btnMines(i);
        if(bombsIndexes.contains(i)) {
            b.isBomb = true;
            b.setIcon(new javax.swing.ImageIcon(
                getClass().getResource(
                    "/minesweeper/resources/bomb.png")));
        }
        b.setPreferredSize(new Dimension(20, 20));

        gamePanel.add(b);
    }
    this.setResizable(false);
    this.setTitle("Minesweeper");
    this.pack(); // Sizes frame so that all components
                 // are at their preferred sizes
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setLocationRelativeTo(null);
    this.validate(); // Recalculate layout
    this.setVisible(true);
}

The problem is when I increase the number of columns, the width of either the gamePanel or mainPanel increases, but the width of the frame does not.

Example:

Enter image description here

How do I change the size of the frame to resize itself based on the panel sizes?

Note: btnMines is basically just a JButton with some variables attached to it.

Bonus question: how would I go about making each button into squares? As you can see, I tried making it square by writing b.setPreferredSize(new Dimension(20, 20));, but each resulting button is still a rectangle!

Also, I know the code is messy. I used to separate that function into separate functions, but now I decided to put it all in one function just to test it out properly.

I tried adding:

this.setResizable(false);
this.setTitle("Minesweeper");
this.pack(); // Sizes frame so that all components
             // are at their preferred sizes
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
this.validate(); // Recalculate layout
this.setVisible(true);

But it didn’t work since the frame didn’t resize!


Solution

  • Frame that contains a Free Designed layout JPanel called mainPanel

    This seems to suggest that mainPanel might be using a null layout, which is going to cause you issues, but this is just a guess.

    JFrame#pack should be wrapping the frame around the content based on the contents "preferred size" hints, provided via the subsequent layout managers, which is leads me to question what layout managers (if any) are involved in your design.

    For example...

    enter image description here

    import java.awt.BorderLayout;
    import java.awt.Dimension;
    import java.awt.EventQueue;
    import java.awt.FlowLayout;
    import java.awt.GridLayout;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    
    public class Main {
        public static void main(String[] args) {
            new Main();
        }
    
        public Main() {
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    JFrame frame = new JFrame();
                    frame.add(new Normalv3(10, 10));
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                }
            });
        }
    
        public class Normalv3 extends JPanel {
    
            public Normalv3(int cols, int rows) {
                setLayout(new BorderLayout());
                add(makeControlsPane(), BorderLayout.NORTH);
                add(makeGamePane(cols, rows));
            }
    
            protected JPanel makeControlsPane() {
                JPanel pane = new JPanel(new FlowLayout(FlowLayout.LEADING));
                pane.add(new JButton("Back to Menu"));
                pane.add(new JButton("Reset"));
                return pane;
            }
    
            protected JPanel makeGamePane(int cols, int rows) {
                JPanel pane = new JPanel(new GridLayout(rows, cols));
                for (int index = 0; index < (cols * rows); index++) {
                    JButton btn = new SquareButton("*");
                    pane.add(btn);
                }
                return pane;
            }
        }
    
        class SquareButton extends JButton {
    
            SquareButton(String s) {
                super(s);
            }
    
            @Override
            public Dimension getPreferredSize() {
                Dimension d = super.getPreferredSize();
                int s = (int) (d.getWidth() < d.getHeight() ? d.getHeight() : d.getWidth());
                return new Dimension(s, s);
            }
        }
    }
    

    Square button design take from Creating Square Buttons in Swing

    As a side note - I'd encourage you NOT to make use of form designers, they tend to become messy very quickly and are difficult to diagnose and maintain (IMHO)