2

I have no purpose for what I'm really doing, just trying things out with Java Swing. What I have right now are these three variables:

    int[] gridIterations = {10,10}; //how many JButtons are made each row/column
    int[] frameSize = {600,600}; //size of the JFrame
    int[] gridSize = {60,60}; //by gridSize, I mean size of the JButtons

I also have a nested for loop which uses these variables to create a grid of JButtons. I would expect the grids to perfectly fit the JFrame, however this is the result:

Grids cut off at the bottom and right edges of JFrame

After some testing I realized that the frame will actually only fit all the JButtons if the size is (615, 631) But I'm wondering, why does it fit only with these parameters, and why, of all numbers, would it be those? To my understanding a simply calculation of 60 * 10 should equal 600 and successfully have all buttons fit into the JFrame, but I am most likely overlooking something. What could that be? Thanks.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
Admin Voter
  • 251
  • 1
  • 12
  • I am not too good with wording; I apologize for the vague title. If someone could edit it to make a bit more... sense? I would greatly appreciate it :) – Admin Voter May 28 '15 at 23:29
  • 1
    The actual frame size accounts for the border as well. – Constant May 28 '15 at 23:33
  • I've tried using `frame.getContentPane().setSize()` but that just starts the frame in that supersmall little window at the top-left. – Admin Voter May 28 '15 at 23:36
  • 1
    Because then you're not setting the frame's size to anything - only the content's size. – Constant May 28 '15 at 23:39

2 Answers2

4

The size of a JFrame includes its insets. This basically means the title bar and borders.

GridLayout will do this perfectly for you with much less effort involved.

grid layout

class GridButtons implements Runnable {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(new GridButtons(4, 5));
    }

    final int rows;
    final int cols;

    GridButtons(int rows, int cols) {
        this.rows = rows;
        this.cols = cols;
    }

    @Override
    public void run() {
        JFrame frame = new JFrame();
        JPanel grid = new JPanel(new GridLayout(rows, cols));

        for (int i = 0; i < (rows * cols); ++i) {
            grid.add(new JButton() {
                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(60, 60);
                }
            });
        }

        frame.setContentPane(grid);
        frame.pack();
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi) – MadProgrammer May 28 '15 at 23:51
  • 1
    @MadProgrammer True, but it works for this particular example. On the other hand, if the buttons had text or an icon I would advocate against it. (But, as always, setting "absolute sizes" means the window will appear a different size on different screen resolutions...) – Radiodef May 28 '15 at 23:53
  • Yeah, but lets try and not encourage people down a path which will lead them into (more) trouble ;) - You could have overridden the `getPreferredSize` method of the `JButton` instead ;) – MadProgrammer May 28 '15 at 23:55
  • @MadProgrammer That's true. I tend to use `set` since it's smaller but I only *happen* to know it behaves the same in *this* case. ; / – Radiodef May 29 '15 at 00:00
  • 1
    Just tied of fix people's code that use `setPreferred/Minimum/MaximumSizse`, so the more I can discourage it, the better ;) - The point of the answer is correct and fine and dispite my little "niggle", I gave it a +1 ;) – MadProgrammer May 29 '15 at 00:17
  • @MadProgrammer I hear that. I've got similar pet peeves of my own. – Radiodef May 29 '15 at 00:31
  • 1
    Thanks for this! In my case I can't use a GridLayout (I was trying to mess around with the `setLayout` set to `null`) but I genuinely thank you for giving me this suggestion for any future project :) – Admin Voter May 29 '15 at 00:35
4

A lot comes down to the requirements of the content and the layout manager. Rather then looking "how big" you'd like the frame to be, focus on the amount of space the content needs. The frame will then "pack" around this.

This means that the frame will "content size + frame border size" big.

JFrame#pack takes into consideration the content's preferred size and adds in the frames border insets automatically.

So, the only thing you need to is call JFrame#pack AFTER you finished adding the content to it

So, based on your code:

public class Testing {

        public static void main(String[] args) {
                JFrame frame = new JFrame("hi");

                for (int i = 0; i < 10; i++) {
                        JButton a = new JButton();
                        a.setSize(20,20);
                        a.setLocation(20*i, 0);
                        frame.getContentPane().add(a);

                }
                frame.setLayout(null);
                frame.pack();
                frame.setVisible(true);
        }

}

You are using a null layout. JFrame#pack will use the information provided by the layout manager to determine the "preferred" size of the overall content.

Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify.

Rather the focusing on the absolute, which would be variable between OS's (and even different PC's with the same OS), focus on the user experience.

As has, already, been demonstrated, you can easily get this to work using a GridLayout

GridLayout

import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
            setLayout(new GridLayout(10, 10, 0, 0));
            for (int index = 0; index < 10 * 10; index++) {
                JButton btn = new JButton(String.valueOf(index)) {
                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(50, 50);
                    }
                };
                add(btn);
            }
        }

    }

}

If, you need a more complex management (ie depth by breadth), you could use a GridBagLayout instead

Take a look at Laying Out Components Within a Container, How to Use GridLayout and How to Use GridBagLayout for more details ;)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Hey, is it meant to have the octothorpe/hash(tag)/tictactoe sign/number sign? Because it shows as "Invalid character" for me :O Sorry by the way, I'm still learning a lot of new things about Java. – Admin Voter May 29 '15 at 00:17
  • 1
    The `#` is just my way saying it's a "instance" field or method (not a static). So yeah, it should normally be `instanceOfJFrame.pack()` ;) – MadProgrammer May 29 '15 at 00:18
  • Ooh I see what you mean. What's the difference between that, and the 2nd option that Constant did though? – Admin Voter May 29 '15 at 00:20
  • 2
    The "main" difference is this is allowing the content (and sub content) to calculate it's preferred size naturally, rather then imposing a size through the use of `setPreferredSize`, which is generally discouraged – MadProgrammer May 29 '15 at 00:22
  • Ah, gotcha, that's what the link you sent previously was about. I guess I have a new answer :P – Admin Voter May 29 '15 at 00:22
  • It doesn't work in this case: http://pastebin.com/3Ms8YRmF. This is what appears: http://i.imgur.com/4OqpCtA.png. Any idea how come? If it is because of the absence of a layout, what would be the workaround (without actually using a layout)? – Admin Voter May 29 '15 at 00:56
  • 1
    `frame.setLayout(null);` <- The "preferred size" is calculated through the layout manager. Stop focusing on the "absolute" and focus on the "user experience" (how it works). Avoid using `null` layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify – MadProgrammer May 29 '15 at 00:58
  • Would anybody care to commit on the reason for the down vote ? In what way does it not answer the ops question? – MadProgrammer May 31 '15 at 21:04