布局未按照 Java 的 Swing 预期进行

Layout not doing what expected in Swing with Java

我是一名学生并且有一个项目应该有一个我已经实现的特定布局,除了由于错误或由于我不知道我做错了什么而导致的某种间距问题而且我已经固定了,所以我无法考虑以不同的方式处理它。它应该是这样的:

我的申请是这样的:

我正在关注 Murach 的 Java 编程,并尝试仅使用本书范围内的任何内容来解决这个问题。

我已将组件添加到适当的面板,并使用 GridBagLayout 将这些组件添加到主面板以组织所有内容。出于某种原因,文本字段和组合框在单选按钮和复选框添加到面板后挂在右侧。我曾尝试创建不同的面板来重新组织、更改布局设置、尝试其他布局、重写整个程序等等。几天前我向我的导师寻求帮助,但他们还没有回复我。我多次阅读了 Swing 的章节,运行 无法在 Google 上搜索。

编辑以添加所有代码:

StudentSurvey.java

package student.timothycdykes.studentsurvey;

public class StudentSurvey {

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(() -> {
            new StudentSurveyFrame();
        });
    }

}

StudentSurveyFrame.java

package student.timothycdykes.studentsurvey;

import java.awt.*;
import javax.swing.*;

public class StudentSurveyFrame extends JFrame {

    public StudentSurveyFrame(){

        try {
            UIManager.setLookAndFeel(
                UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException | InstantiationException |
                 IllegalAccessException | UnsupportedLookAndFeelException e) {
            System.err.println(e);
        }

        initComponents();
    }

    private void initComponents(){
        setTitle("Student Survey");
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationByPlatform(true);
//        setLayout(new FlowLayout(FlowLayout.LEFT));

        // Text Fields
        Dimension dim = new Dimension(150,20);
        JTextField firstNameTextField = new JTextField();
        JTextField lastNameTextField = new JTextField();
        firstNameTextField.setPreferredSize(dim);
        firstNameTextField.setMinimumSize(dim);
        lastNameTextField.setPreferredSize(dim);
        lastNameTextField.setMinimumSize(dim);

        // Combo Box
        String[] countriesList = {"Select a country...", 
            "Albania", 
            "Bulgaria", 
            "Congo", 
            "Guernsey", 
            "Jamaica", 
            "Korea", 
            "Mozambique", 
            "Oman", 
            "Philippines", 
            "United States", 
            "Other"};
        JComboBox countriesComboBox = new JComboBox(countriesList);

        // Radio Buttons
        ButtonGroup eyeColorButtonGroup = new ButtonGroup();
        JRadioButton brownRadioButton = new JRadioButton("Brown");
        JRadioButton greenRadioButton = new JRadioButton("Green");
        JRadioButton blueRadioButton = new JRadioButton("Blue");
        JRadioButton otherRadioButton = new JRadioButton("Other");
        eyeColorButtonGroup.add(brownRadioButton);
        eyeColorButtonGroup.add(greenRadioButton);
        eyeColorButtonGroup.add(blueRadioButton);
        eyeColorButtonGroup.add(otherRadioButton);
        JPanel radioButtonPanel = new JPanel();
        //radioButtonPanel.setBorder(BorderFactory.createEmptyBorder());
        radioButtonPanel.add(brownRadioButton);
        radioButtonPanel.add(greenRadioButton);
        radioButtonPanel.add(blueRadioButton);
        radioButtonPanel.add(otherRadioButton);

        // Check Boxes
        JCheckBox HTMLCheckBox = new JCheckBox("HTML");
        JCheckBox SQLCheckBox = new JCheckBox("SQL");
        JCheckBox javaCheckBox = new JCheckBox("Java");
        JCheckBox androidCheckBox = new JCheckBox("Android");
        JCheckBox pythonCheckBox = new JCheckBox("Python");
        JPanel checkBoxPanel = new JPanel();
        //checkBoxPanel.setBorder(BorderFactory.createEmptyBorder());
        checkBoxPanel.add(HTMLCheckBox);
        checkBoxPanel.add(SQLCheckBox);
        checkBoxPanel.add(javaCheckBox);
        checkBoxPanel.add(androidCheckBox);
        checkBoxPanel.add(pythonCheckBox);

        // Buttons
        JButton submitButton = new JButton("Submit");
        submitButton.addActionListener(e -> {

            // Create a message to append data to
            String message = "Thanks for taking our survey!\n\n"
                    + "Here's the data you entered:\n";


            // Get the name
            String firstName = firstNameTextField.getText();
            String lastName = lastNameTextField.getText();

            // Get the country
            int countryIndex = countriesComboBox.getSelectedIndex();
            String country = countriesList[countryIndex];

            // Get the eye color
            String eyeColor = "";
            if (brownRadioButton.isSelected()) {
                eyeColor = "Brown";
            } else if (greenRadioButton.isSelected()) {
                eyeColor = "Green";
            } else if (blueRadioButton.isSelected()) {
                eyeColor = "Blue";
            } else if (otherRadioButton.isSelected()) {
                eyeColor = "Other";
            }

            // Get the skills
            String skills = "";
            if (HTMLCheckBox.isSelected()) {
                skills += "HTML";
            }
            if (SQLCheckBox.isSelected()) {
                skills += ", SQL";
            }
            if (javaCheckBox.isSelected()) {
                skills += ", Java";
            }
            if (androidCheckBox.isSelected()) {
                skills += ", Android";
            }
            if (pythonCheckBox.isSelected()) {
                skills += ", Python";
            }

            // Validate, append to message, and show a dialog box
            if (Validator.isEmpty(firstName, "First name") && Validator.isEmpty(lastName, "Last name")
                    && Validator.isZeroIndex(countryIndex, "Country") && Validator.isEmpty(eyeColor, "Eye color")) {
                message += "Name: " + firstName + " " + lastName + "\n";
                message += "Country: " + country + "\n";
                message += "Eye color: " + eyeColor + "\n";
                message += "Skills: " + skills;
                JOptionPane.showMessageDialog(this, message, "Message", JOptionPane.INFORMATION_MESSAGE);
            }

        });
        JButton exitButton = new JButton("Exit");
        exitButton.addActionListener(e -> {
           System.exit(0); 
        });
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
        buttonPanel.add(submitButton);
        buttonPanel.add(exitButton);

        // Grid Panel
        JPanel northGridPanel = new JPanel();
        northGridPanel.setLayout(new GridBagLayout());
        northGridPanel.add(new JLabel("First Name:"), getConstraints(0,0));
        northGridPanel.add(firstNameTextField, getConstraints(1,0));
        northGridPanel.add(new JLabel("Last Name:"), getConstraints(0,1));
        northGridPanel.add(lastNameTextField, getConstraints(1,1));
        northGridPanel.add(new JLabel("Country:"), getConstraints(0,2));
        northGridPanel.add(countriesComboBox, getConstraints(1,2));
        northGridPanel.add(new JLabel("Eye color:"), getConstraints(0,3));
        northGridPanel.add(radioButtonPanel, getConstraints(0,4));
        northGridPanel.add(new JLabel("Programming skills:"), getConstraints(0,5));
        northGridPanel.add(checkBoxPanel, getConstraints(0,6));

        // Construct the frame
        add(northGridPanel, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.SOUTH);
        pack();
        setVisible(true);
        setLocationRelativeTo(null);
    }

    private GridBagConstraints getConstraints(int x, int y) {
        GridBagConstraints c = new GridBagConstraints();
        c.anchor = GridBagConstraints.LINE_START;
        c.insets = new Insets(5, 5, 0, 5);
        c.gridx = x;
        c.gridy = y;
        return c;
    }
}

Validator.java

package student.timothycdykes.studentsurvey;

import javax.swing.JOptionPane;

public class Validator {

    private static void generateErrorDialog(String field) {
        String message = "";
        message += field + " is a required field."
                + "\nPlease complete this field before submitting.";
        JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
    }

    public static boolean isEmpty(String string, String field) {
        boolean isValid = true;
        if (string.equals("")) {
            isValid = false;
            generateErrorDialog(field);
        }
        return isValid;
    }

    public static boolean isZeroIndex(int index, String field) {
        boolean isValid = true;
        if(index == 0) {
            isValid = false;
            generateErrorDialog(field);
        }
        return isValid;
    }

}

我想补充一点,我知道代码没有遵循最佳实践。本书中的某些 material 已过时,其中一些正是教师要求的内容。

The text fields and combo box, for some reason, hang to the right after the radio buttons and check boxes are added to the panel.

您需要了解跨单元格的概念。

网格中的每个单元格都是添加到列中的最大组件的大小。

所以您的单选按钮和复选框面板是第一列中最大的组件,因此其他组件显示在这些组件右侧的第二列中。

因此,当您为这两个面板创建约束时,您需要指定每个面板占据两列的 space。

阅读 Swing 教程中关于 How to Use GridBagLayout 的部分,具体来说,您需要查看 gridwidth 约束。

It seems the panels containing the check boxes and radio buttons should span 2 columns of the grid bag layout.

我的猜测是正确的。这是实施该建议的 MRE。我还更改了其中一个文本字段的宽度,以演示不同列大小的效果。

import java.awt.*;
import javax.swing.*;

/* Do NOT extend components, containers or windows without good cause. It is
 done here in order to stick to the spirit of the code in the question. */
public class LayoutProblemGBL extends JFrame {

    LayoutProblemGBL() {
        initComponents();
    }

    private void initComponents() {
        setTitle("Student Survey");
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationByPlatform(true);

        // Text Fields
        //Dimension dim = new Dimension(150, 20);
        JTextField firstNameTextField = new JTextField(20);
        JTextField lastNameTextField = new JTextField(18);

        // Combo Box
        String[] countriesList = {"Select a country...",
            "Albania",
            "United States"};
        JComboBox countriesComboBox = new JComboBox(countriesList);

        // Radio Buttons
        JRadioButton brownRadioButton = new JRadioButton("Brown");
        JRadioButton greenRadioButton = new JRadioButton("Green");
        JRadioButton blueRadioButton = new JRadioButton("Blue");
        JRadioButton otherRadioButton = new JRadioButton("Other");
        JPanel radioButtonPanel = new JPanel();
        radioButtonPanel.add(brownRadioButton);
        radioButtonPanel.add(greenRadioButton);
        radioButtonPanel.add(blueRadioButton);
        radioButtonPanel.add(otherRadioButton);

        // Check Boxes
        JCheckBox HTMLCheckBox = new JCheckBox("HTML");
        JCheckBox SQLCheckBox = new JCheckBox("SQL");
        JCheckBox javaCheckBox = new JCheckBox("Java");
        JCheckBox androidCheckBox = new JCheckBox("Android");
        JCheckBox pythonCheckBox = new JCheckBox("Python");
        JPanel checkBoxPanel = new JPanel();
        checkBoxPanel.add(HTMLCheckBox);
        checkBoxPanel.add(SQLCheckBox);
        checkBoxPanel.add(javaCheckBox);
        checkBoxPanel.add(androidCheckBox);
        checkBoxPanel.add(pythonCheckBox);

        // Buttons
        JButton submitButton = new JButton("Submit");
        JButton exitButton = new JButton("Exit");
        JPanel buttonPanel = new JPanel();
        buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
        buttonPanel.add(submitButton);
        buttonPanel.add(exitButton);

        // Grid Panel
        JPanel northGridPanel = new JPanel();
        northGridPanel.setLayout(new GridBagLayout());
        northGridPanel.add(new JLabel("First Name:"), getConstraints(0, 0));
        northGridPanel.add(firstNameTextField, getConstraints(1, 0));
        northGridPanel.add(new JLabel("Last Name:"), getConstraints(0, 1));
        northGridPanel.add(lastNameTextField, getConstraints(1, 1));
        northGridPanel.add(new JLabel("Country:"), getConstraints(0, 2));
        northGridPanel.add(countriesComboBox, getConstraints(1, 2));
        northGridPanel.add(new JLabel("Eye color:"), getConstraints(0, 3));
        northGridPanel.add(radioButtonPanel, getConstraints(0, 4, 2));
        northGridPanel.add(new JLabel("Programming skills:"), getConstraints(0, 5));
        northGridPanel.add(checkBoxPanel, getConstraints(0, 6, 2));

        // Construct the frame
        add(northGridPanel, BorderLayout.CENTER);
        add(buttonPanel, BorderLayout.SOUTH);
        pack();
        setVisible(true);
        setLocationRelativeTo(null);
    }

    private GridBagConstraints getConstraints(int x, int y) {
        return getConstraints(x, y, 1);
    }

    private GridBagConstraints getConstraints(int x, int y, int width) {
        GridBagConstraints c = new GridBagConstraints();
        c.anchor = GridBagConstraints.LINE_START;
        c.insets = new Insets(5, 5, 0, 5);
        c.gridx = x;
        c.gridy = y;
        c.gridwidth = width;
        return c;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            new LayoutProblemGBL();
        };
        SwingUtilities.invokeLater(r);
    }
}