4

Is setting preferred size of JFrame using setPreferredSize() considered bad?

If it is bad, what is good way to change JFrame window size to dimension I need.

I know laying components in way to reflect final JFrame dimension that I need. But if I use shortcut to change preferred size using call setPreferredSize() just before call to pack() to change final JFrame size is bad? If so why?

For example I have sample form:

enter image description here

This is displayed without setting preferred size.

Now I can resize form with call to setPreferredSize() before call to pack().

enter image description here

This is displayed with call: setPreferredSize(new Dimension(500, 300));

I can have similar effect with setting components size while laying it out. But what is disadvantage of setting frame size with just call to setPreferredSize().

I can think setting preferred size as resizing displayed window manually with mouse after it has been displayed. Isn't it?

Code:

import java.awt.Dimension;

import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class MyFrame extends JFrame {
    private static final long serialVersionUID = 1L;

    private JTextField fullNameTextField = new JTextField();
    private JTextField emailIDTextField = new JTextField();
    private JTextArea addressTextArea = new JTextArea();
    private JButton submitButton = new JButton("Submit");
    private JButton cancelButton = new JButton("Cancel");

    public MyFrame(){
        super("MyFrame");

        layoutComponents();

        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationByPlatform(true);

        setPreferredSize(new Dimension(500, 300));

        pack();
    }

    private void layoutComponents(){
        GroupLayout layout = new GroupLayout(getContentPane());
        getContentPane().setLayout(layout);

        JLabel fullNameLabel = new JLabel("Full Name:");
        JLabel emailIDLabel = new JLabel("Email ID:");
        JLabel addressLabel = new JLabel("Address:");

        JScrollPane addressScrollPane = new JScrollPane(addressTextArea);

        layout.setHorizontalGroup(
                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                        .addGap(10)
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                                .addGroup(layout.createSequentialGroup()
                                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                                                .addComponent(fullNameLabel)
                                                .addComponent(emailIDLabel)
                                                .addComponent(addressLabel)
                                            )
                                        .addGap(15)
                                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                                                .addComponent(fullNameTextField, 10, 200, Short.MAX_VALUE)
                                                .addComponent(emailIDTextField)
                                                .addComponent(addressScrollPane)
                                                .addGroup(layout.createSequentialGroup()
                                                        .addGap(0, 0, Short.MAX_VALUE)
                                                        .addComponent(submitButton)
                                                        .addGap(10)
                                                        .addComponent(cancelButton)
                                                    )
                                            )
                                    )
                            )
                        .addGap(10)
                    )
            );

        layout.setVerticalGroup(
                layout.createParallelGroup(GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                        .addGap(10)
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                .addComponent(fullNameLabel)
                                .addComponent(fullNameTextField)
                            )
                        .addGap(5)
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                .addComponent(emailIDLabel)
                                .addComponent(emailIDTextField)
                            )
                        .addGap(5)
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                .addComponent(addressLabel)
                                .addComponent(addressScrollPane, 20, 60, Short.MAX_VALUE)
                            )
                        .addGap(15)
                        .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                                .addComponent(submitButton)
                                .addComponent(cancelButton)
                            )
                        .addGap(10)
                    )
            );

        layout.linkSize(submitButton, cancelButton);

        getRootPane().setDefaultButton(submitButton);
    }

    public static void main(String [] args){
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new MyFrame().setVisible(true);
            }
        });
    }
}
nullptr
  • 3,320
  • 7
  • 35
  • 68
  • 1
    The main reason it would be considered bad is the use of unknown (magic) numbers, rather then empirical values. Every platform (and even similar OS running on different hardware and settings) has it's own means for rendering content which can change the amount of space that individual components require. In regards to things like text fields and text areas, you make suggestions about the number of columns (and rows for text areas) which can be used to change a frames final size. – MadProgrammer Nov 09 '14 at 08:25
  • I had developer that did this kind of thing, which blow up in our faces when users were using resolutions smaller than he had designed for (an additional problem was the screen was over packed and badly designed), but with some tweaking and simply relying on `pack` and the layout management API, we resolved the issue rather adequately... – MadProgrammer Nov 09 '14 at 08:30
  • @MadProgrammer I know I can have same effect with suggesting text field's preferred sizes and text area's columns, BUT that sizes are also prone to problems on different hardwares and settings. I am technically not convinced with resizing, if I have layout all components well. – nullptr Nov 09 '14 at 08:43
  • The other problem I see has to do with the ability for other people to override it, whereas overriding `getPreferredSize` can offer you some protection against tampering, accidental or otherwise... – MadProgrammer Nov 09 '14 at 08:44
  • I'm not suggesting setting the preferred size's at all, I've suggesting providing hints that are used by the components to make their internal calculations, which ARE based on information obtained from the current system (`setColumns` and `setRows`) – MadProgrammer Nov 09 '14 at 08:45
  • If you don't actually know whether it's considered bad, why are you asking why it's considwred bad? – user207421 Nov 09 '14 at 08:46
  • @EJP I am not saying it bad, but people told me its bad, and so saying why people considering it bad. – nullptr Nov 09 '14 at 08:47
  • @MadProgrammer sorry I meant suggesting layout manager about sizes of component like I did `.addComponent(fullNameTextField, 10, 200, Short.MAX_VALUE)` – nullptr Nov 09 '14 at 08:51
  • @Meraman Yeah, I don't like `GroupLayout`, I'd prefer to use `GridBagLayout` or `MigLayout`, but that's me... – MadProgrammer Nov 09 '14 at 08:53
  • @MadProgrammer LOL, I love `GroupLayout`, it allows me to lay out components in a way I want. But its complex and difficult. – nullptr Nov 09 '14 at 08:54
  • @Meraman You should try `MigLayout` then, I think it's more readable than `GroupLayout`, `GroupLayout` and `SpringLayout` were designed more for form editors then hand coding from what I understand... – MadProgrammer Nov 09 '14 at 09:02
  • @MadProgrammer Sure I will give these try. I learnt only GroupLayout, inspired from GUI builders like NetBeans, and is sufficient for my purposes, and I found it much better for persisting complex GUI style even after user resizes window. – nullptr Nov 09 '14 at 09:06
  • 1
    @Meraman *"I learnt only GroupLayout"* - That seems weird to me, when you also suggest that we're not laying out our layouts properly to get the same effect. If all you have is a hammer, then every problem have is a nail... – MadProgrammer Nov 09 '14 at 12:48
  • Possible [duplicate](http://stackoverflow.com/q/7229226/230513). – trashgod Nov 09 '14 at 13:08
  • The main issue here can be answered by going to the post trashgod suggested. Bottom line: learn layout managers and the effect these methods (i.e. `setPreferredSize()`) will have, if any. Some layout managers ignore these altogether. – hfontanez Nov 09 '14 at 13:22

2 Answers2

7

The main reason it would be considered bad (bad is probably too strong a word, unwise might be better) is the use of unknown (magic) numbers, rather than empirical values.

Every platform (and even similar OS running on different hardware and settings) has it's own means for rendering content which can change the amount of space that individual components require.

In regards to things like text fields and text-areas, you can make suggestions about the number of columns (and rows for text areas) which can be used to change a frames final size, using setColumns and setRows for example...

So, using the following code...

import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestLayout101 {

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

    public TestLayout101() {
        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 {

        private JTextField fullNameTextField = new JTextField(10);
        private JTextField emailIDTextField = new JTextField(10);
        private JTextArea addressTextArea = new JTextArea(10, 20);
        private JButton submitButton = new JButton("Submit");
        private JButton cancelButton = new JButton("Cancel");

        public TestPane() {

            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.WEST;
            gbc.insets = new Insets(4, 4, 4, 4);

            add(new JLabel("Full Name: "), gbc);
            gbc.gridy++;
            add(new JLabel("Email ID: "), gbc);
            gbc.gridy++;
            gbc.anchor = GridBagConstraints.NORTHWEST;
            add(new JLabel("Address: "), gbc);

            gbc.gridx++;
            gbc.gridy = 0;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.weightx = 1;

            add(fullNameTextField, gbc);
            gbc.gridy++;
            add(emailIDTextField, gbc);
            gbc.gridy++;
            gbc.weighty = 1;
            add(new JScrollPane(addressTextArea), gbc);

            JPanel buttons = new JPanel(new FlowLayout(FlowLayout.RIGHT, 4, 4));
            buttons.add(submitButton);
            buttons.add(cancelButton);

            gbc.gridx = 0;
            gbc.gridy++;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.weighty = 0;
            add(buttons, gbc);
        }
    }
}

Which produces...

Compact

Now by changing only one line...

private JTextArea addressTextArea = new JTextArea(10, 20);
                                // Only this value ---^

It produces this...

Mini

And again...

private JTextArea addressTextArea = new JTextArea(10, 40);
                                // Only this value ---^

Luxury

I could change the number of rows for the JTextArea and effect the height of the window as well.

The difference is, these values are used in combination with the systems font metrics to calculate the preferred size of the components when the program is run, so it will be different for different systems and platforms...

The main point to layouts is to spend your time focused on the intent and the work flow and not trying to get a pixel perfect solution, because there's simply no such thing...talk to web developers, they have the same issues, just much worse (multiple browsers on a single system, all rendering differently)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • **systems font metrics to calculate the preferred size** makes lot sense. I got the point I think. Thanks for good answer. – nullptr Nov 09 '14 at 18:37
1

Personally, I think there is nothing wrong with using frame.setPreferredSize() to resize a JFrame window. In fact, I think it is the most appropriate solution for resizing it.

But this is a good time to make a distinction between what some of the most common resizing methods do:

  • frame.pack(), as explained here basically takes the prefered sizes for each of the individual elements.

  • frame.setSize() resizes the window as expected, but only if a component's parent has no layout manager expressly defined. There is a discussion concerning the use of this method here.

That doesn't really leave any other methods to use to define the size of a window, which is why I think setting the preferred window size is the best way go about it. I would still make sure to define setMinimumSize() and setMaximumSize() to ensure the components are resized correctly in all cases.

Edit:

Other posts in this thread got me thinking about the use of 'magic constants' in setting a components. In web development, for instance, it is generally frowned upon to use pixels, for example, as this changes widely by system. I wanted to see if this was also true with dimensions. In short, my findings concluded it doesn't.

This was a JFrame with a prefered size of new Dimension(200, 300) taken on my MacBook Pro with Retina Display:

JFrame with a <code>prefered size</code> of <code>new Dimension(200, 300)</code>

This was a JFrame with a prefered size of new Dimension(200, 300) taken on my Asus Windows Tablet (non-retina display):

JFrame with a <code>prefered size</code> of <code>new Dimension(200, 300)</code>

These windows looked the exact same and, although I couldn't find this published, the Dimenion is really an already-scaled proportion, taking into consideration the height, width, and resolution of the display to produce a near accurate rendering across all devices. This, I guess you could say, makes sense because it is the Java way.

That being said, here's some things I think need to watched out for:

  1. The difference between UIManager LookAndFeel across platforms as MadProgrammer pointed out

Honestly, I think the final decision is left up to the programmer. After this testing, I pretty much still conclude that using the frame.setPreferredSize() is one of the best things available if you don't intend to a implement a semi-complex window manager. Even then, I think there will always be some unaccounted variation.

Community
  • 1
  • 1
James Taylor
  • 6,158
  • 8
  • 48
  • 74
  • 2
    But you're still relying on "magic numbers" and not empirical values, what looks good your screen won't always work on other systems or platforms... – MadProgrammer Nov 09 '14 at 08:43
  • I edited my post to address this. I'm not convinced that's always a severe problem. – James Taylor Nov 09 '14 at 16:44
  • But, where's the content? A blank frame will, mostly, look the same across most platforms, without some actual content, how can you tell. Having developed a single application to work across Windows XP, 7 and 8 and MacOS, we've had lots of fun with little oddities of things not appearing where expected or the way they were expected. My predecessor was a classic for using setPreferredSize, so much so, we now have a debug mode which changes the font and font size so we can run through the program and test the layouts – MadProgrammer Nov 09 '14 at 19:45
  • You might like to check out the updated screenshot comparisons between Windows and MacOS I was able to get... – MadProgrammer Nov 09 '14 at 23:40