Which Layout Manager to use? [closed]

2019-02-20 20:59发布

问题:

So basically I want to create this sort of a GUI, but because of my inexperience with Java GUIs, I cannot figure out which Layout Manager to use. I've tried Flow, Border, Grid, but none of them allow me to create this sort of a GUI without messing up the alignments somewhere.

Any suggestions? How should I decide on a layout manager in the future, or is it something which will come with experience?

I'd prefer to use a simple to use layout, as this is a very basic GUI and I don't think something like MiGLayout should be necessary.

回答1:

I'd use a combination of compound panels and layouts. Apart from making easier to get the layout to work, you could also isolate areas of responsibility within their own class.

public class TestLayout13 {

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

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

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new FormPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class FormPane extends JPanel {

        public FormPane() {
            setBorder(new EmptyBorder(8, 8, 8, 8));
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.fill = GridBagConstraints.BOTH;
            gbc.weightx = 1;

            NamePane namePane = new NamePane();
            namePane.setBorder(new CompoundBorder(new TitledBorder("Name"), new EmptyBorder(4, 4, 4, 4)));
            add(namePane, gbc);

            gbc.gridy++;

            EMailPane emailPane = new EMailPane();
            emailPane.setBorder(new CompoundBorder(new TitledBorder("E-Mail"), new EmptyBorder(4, 4, 4, 4)));
            add(emailPane, gbc);
        }

    }

    public class NamePane extends JPanel {

        public NamePane() {
            setLayout(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.EAST;

            add(new JLabel("First Name:"), gbc);
            gbc.gridx += 2;
            add(new JLabel("Last Name:"), gbc);
            gbc.gridy++;
            gbc.gridx = 0;
            add(new JLabel("Title:"), gbc);
            gbc.gridx += 2;
            add(new JLabel("Nickname:"), gbc);

            gbc.gridx = 1;
            gbc.gridy = 0;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.anchor = GridBagConstraints.WEST;
            gbc.weightx = 0.5;
            add(new JTextField(10), gbc);
            gbc.gridx += 2;
            add(new JTextField(10), gbc);
            gbc.gridy++;
            gbc.gridx = 1;
            add(new JTextField(10), gbc);
            gbc.gridx += 2;
            add(new JTextField(10), gbc);

            gbc.gridx = 0;
            gbc.gridy++;
            gbc.anchor = GridBagConstraints.EAST;
            gbc.weightx = 0;
            gbc.fill = GridBagConstraints.NONE;
            add(new JLabel("Format:"), gbc);

            gbc.anchor = GridBagConstraints.WEST;
            gbc.gridx++;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            add(new JComboBox(), gbc);            
        }        
    }

    protected class EMailPane    extends JPanel {

        public EMailPane() {
            JPanel detailsPane = new JPanel(new GridBagLayout());
            GridBagConstraints gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.anchor = GridBagConstraints.EAST;
            detailsPane.add(new JLabel("E-Mail Address:"), gbc);

            gbc.gridx++;
            gbc.anchor = GridBagConstraints.WEST;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;
            detailsPane.add(new JTextField(10), gbc);

            gbc.gridy++;
            gbc.gridx = 0;
            gbc.fill = GridBagConstraints.BOTH;
            gbc.weighty = 1;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            detailsPane.add(new JScrollPane(new JList()), gbc);

            JPanel buttonsPane = new JPanel(new GridBagLayout());
            gbc = new GridBagConstraints();
            gbc.gridx = 0;
            gbc.gridy = 0;
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.HORIZONTAL;

            buttonsPane.add(new JButton("Add"), gbc);
            gbc.gridy++;
            buttonsPane.add(new JButton("Edit"), gbc);
            gbc.gridy++;
            buttonsPane.add(new JButton("Delete"), gbc);

            gbc.gridy++;
            gbc.weighty = 1;
            gbc.anchor = GridBagConstraints.NORTH;
            buttonsPane.add(new JButton("As Default"), gbc);

            JPanel formatPane = new JPanel(new FlowLayout(FlowLayout.LEFT));
            formatPane.setBorder(new TitledBorder(new EmptyBorder(1, 1, 1, 1), "Mail Format:"));
            formatPane.add(new JRadioButton("HTML"));
            formatPane.add(new JRadioButton("Plain"));
            formatPane.add(new JRadioButton("Custom"));

            setLayout(new BorderLayout());
            add(detailsPane);
            add(buttonsPane, BorderLayout.LINE_END);
            add(formatPane, BorderLayout.PAGE_END);                
        }            
    }        
}


回答2:

My preference is MigLayout because it is the most complete and well documented layout manager for Swing. In addition to Swing, it also supports SWT and JavaFX, so the time you spend learning it may pay back more than once. It supports maven, refer to http://www.miglayout.com for details, which is a big plus. Also it has a very useful debug feature. You can add "debug 1" in the constructor, and you will se how the layout was created. Example:

emailButtonPanel.setLayout(new MigLayout("wrap, fill, insets 20 10 0 10, debug 1"));

Another feature that I find very useful from time to time, is the hidemode, which allows you that a component does not take part in the layout if not visible (or several other strategies).

Here is one possible approach for the image you posted, using MigLayout:

import java.awt.*;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;

import net.miginfocom.layout.CC;
import net.miginfocom.swing.MigLayout;

/**
 */
public class LayoutApproach {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame("Contact information");
                frame.getContentPane().add(new ContactPanel());
                frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
                frame.setMinimumSize(new Dimension(800, 450));
                frame.setLocationRelativeTo(null); // Center
                frame.pack();
                frame.setVisible(true);
            }
        });
    }

    static class ContactPanel extends JPanel {

        private JPanel namePanel;
        private TitledBorder nameTitledBorder;
        private JLabel firstNameLabel;
        private JTextField firstNameTextField;

        private JLabel lastNameLabel;
        private JTextField lastNameTextField;

        private JLabel titleLabel;
        private JTextField titleTextField;

        private JLabel nicknameLabel;
        private JTextField nickNameTextField;

        private JLabel formatLabel;
        private JComboBox<String> formatComboBox;

        private JPanel emailPanel;
        private TitledBorder emailTitledBorder;
        private JLabel emailLabel;
        private JTextField emailTextField;
        private JList<String> emailItemsList;
        private JLabel mailFormatLabel;
        private JPanel emailButtonPanel;
        private JButton addButton;
        private JButton editButton;
        private JButton removeButton;
        private JButton asDefaultButton;

        private JRadioButton htmlRadioButton;
        private JRadioButton plainTextRadioButton;
        private JRadioButton customTextRadioButton;

        private JPanel buttonPanel;

        private JButton okButton;
        private JButton cancelButton;

        public ContactPanel() {
            createComponents();
            makeLayout();
            createHandlers();
            registerHandlers();
            initComponent();
            i18n();
        }

        /**
         * Create GUI components, but contains no layout.
         */
        public void createComponents() {

            namePanel = new JPanel();
            nameTitledBorder = new TitledBorder("");
            firstNameLabel = new JLabel();
            firstNameTextField = new JTextField();
            lastNameLabel = new JLabel();
            lastNameTextField = new JTextField();
            titleLabel = new JLabel();
            titleTextField = new JTextField();
            nicknameLabel = new JLabel();
            nickNameTextField = new JTextField();
            formatLabel = new JLabel();
            formatComboBox = new JComboBox<>();

            emailPanel = new JPanel();
            emailTitledBorder = new TitledBorder("");
            emailLabel = new JLabel();
            emailTextField = new JTextField();
            emailItemsList = new JList<>();
            mailFormatLabel = new JLabel();
            emailButtonPanel = new JPanel();
            addButton = new JButton();
            editButton = new JButton();
            removeButton = new JButton();
            asDefaultButton = new JButton();

            htmlRadioButton = new JRadioButton();
            plainTextRadioButton = new JRadioButton();
            customTextRadioButton = new JRadioButton();

            buttonPanel = new JPanel();
            okButton = new JButton();
            cancelButton = new JButton("Cancel");
        }

        /**
         * Create listeners/handlers
         */
        public void createHandlers() {
        }

        /**
         * Registers/adds listeners/handlers.
         */
        public void registerHandlers() {
        }

        public void makeLayout() {
            layoutNamePanel();
            layoutEmailPanel();
            layoutButtonPanel();

            MigLayout migLayout = new MigLayout("fill, insets 20");
            setLayout(migLayout);
            add(namePanel, "dock north");
            add(emailPanel, "dock north");
            add(buttonPanel, "dock south");
        }

        private void layoutButtonPanel() {
            buttonPanel.setLayout(new MigLayout("alignX right"));
            buttonPanel.add(okButton, "tag ok");
            buttonPanel.add(cancelButton, "tag cancel");
        }

        private void layoutNamePanel() {
            MigLayout nameLayout = new MigLayout("fill, wrap 4",               // Layout Constraints
                                                 "15[]15[grow]15[]15[grow]",   // Column constraints
                                                 "");                          // Row constraints

            // -- Layout all components with name
            namePanel.setLayout(nameLayout);
            // Create this border here since I use it for layout
            Border nameBorder = BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10, 6, 10, 6), nameTitledBorder);
            namePanel.setBorder(nameBorder);
            namePanel.add(firstNameLabel, "alignX right");
            namePanel.add(firstNameTextField, "grow");
            namePanel.add(lastNameLabel, "alignX right");
            namePanel.add(lastNameTextField, "grow");

            namePanel.add(titleLabel, "alignX right");
            namePanel.add(titleTextField, "grow");
            namePanel.add(nicknameLabel, "alignX right");
            namePanel.add(nickNameTextField, "grow");

            namePanel.add(formatLabel, "alignX right");
            namePanel.add(formatComboBox, new CC().grow().span(3));  // Alternative to using plain text'
        }

        private void layoutEmailPanel() {
            MigLayout emailLayout = new MigLayout("fill",// Layout Constraints
                                                  "",    // Column constraints
                                                  "");   // Row constraints

            // -- Layout all components with name
            emailPanel.setLayout(emailLayout);
            // Create this border here since I use it for layout
            Border emailBorder = BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(10, 6, 10, 6), emailTitledBorder);
            emailPanel.setBorder(emailBorder);

            emailButtonPanel.setLayout(new MigLayout("wrap, fill, insets 20 10 0 10"));
            emailButtonPanel.add(addButton, "growx");
            emailButtonPanel.add(editButton, "growx");
            emailButtonPanel.add(removeButton, "growx");
            emailButtonPanel.add(asDefaultButton, "growx");

            JPanel emailAndItems = new JPanel(new MigLayout("fill"));
            emailAndItems.add(emailLabel, "split 2");
            emailAndItems.add(emailTextField, "span, growx, wrap");
            emailAndItems.add(emailItemsList, "span, grow");

            JPanel radioButtons = new JPanel(new MigLayout());
            radioButtons.add(htmlRadioButton);
            radioButtons.add(plainTextRadioButton);
            radioButtons.add(customTextRadioButton);

            emailPanel.add(radioButtons, "dock south");
            emailPanel.add(mailFormatLabel, "dock south, gapleft 15");
            emailPanel.add(emailAndItems, "dock west, growx, push");
            emailPanel.add(emailButtonPanel, "dock east, shrink");

            ButtonGroup buttonGroup = new ButtonGroup();
            buttonGroup.add(htmlRadioButton);
            buttonGroup.add(plainTextRadioButton);
            buttonGroup.add(customTextRadioButton);
        }

        /**
         * Sets initial values for component.
         */
        public void initComponent() {
            formatComboBox.addItem("Item 1");
            formatComboBox.addItem("Item 2");
            formatComboBox.addItem("Item 3");
            formatComboBox.addItem("Item 4");

            DefaultListModel<String> model = new DefaultListModel<>();
            emailItemsList.setModel(model);
            model.insertElementAt("Item 1", 0);
            model.insertElementAt("Item 2", 1);
            model.insertElementAt("Item 3", 2);
            model.insertElementAt("Item 4", 3);

            customTextRadioButton.setSelected(true);
        }

        public void i18n() {
            nameTitledBorder.setTitle("Name:");
            firstNameLabel.setText("First Name:");
            lastNameLabel.setText("Last Name:");
            titleLabel.setText("Title:");
            nicknameLabel.setText("Nickname:");
            formatLabel.setText("Format:");

            emailTitledBorder.setTitle("E-mail");
            emailLabel.setText("E-mail address:");
            mailFormatLabel.setText("Mail Format:");
            addButton.setText("Add");
            editButton.setText("Edit");
            removeButton.setText("Remove");
            asDefaultButton.setText("As Default");

            htmlRadioButton.setText("HTML");
            plainTextRadioButton.setText("Plain Text");
            customTextRadioButton.setText("Custom");

            okButton.setText("OK");
            cancelButton.setText("Cancel");
        }
    }
}

The code was written in Java 7. A maven pom.xml file to go with it could be something like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>stackoverflow</groupId>
    <artifactId>stackoverflow</artifactId>
    <version>1.0</version>


    <dependencies>
        <dependency>
            <groupId>com.miglayout</groupId>
            <artifactId>miglayout-core</artifactId>
            <version>4.2</version>
        </dependency>

        <dependency>
            <groupId>com.miglayout</groupId>
            <artifactId>miglayout-swing</artifactId>
            <version>4.2</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>


回答3:

I'd say border layout will work alright for you if you are looking for Java library layout managers. A more advanced one would be MiG Layout. You can google it for the info.

You do know that you can nest layout managers right? As in, one JPanel has one layoutmanager and another has a different one. So one could have border layout and another flow layout.



回答4:

You can try NetBeans if you want a simple Swing designer, but IMHO Miglayout is the shortest way to get your UI's done as you intended. After the initial learning curve of course...

Try to visualize your screen in grids, and then you can use both gridbag and miglayout.