简体   繁体   中英

Why setting setPreferredSize() on JFrame is bad?

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:

在此输入图像描述

This is displayed without setting preferred size.

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

在此输入图像描述

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);
            }
        });
    }
}

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...

紧凑

Now by changing only one line...

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

It produces this...

微型

And again...

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

豪华

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)

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:

具有<code>首选大小</ code>的<code> new Dimension(200,300)</ code>的JFrame

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

具有<code>首选大小</ code>的<code> new Dimension(200,300)</ code>的JFrame

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM