布局管理器,用于垂直和水平排列可变大小的组件

Layout Manager for lining up variably-sized components vertically and horizontally

我正在尝试为数据库中的 adding/editing 用户制作一个相当基本的面板。为了做到这一点,我想在左侧为不同的字段设置多个标签,在右侧为该字段设置相应的控件。 (因此,左侧可能会显示 "Username"、"User Type"、"Expiration Date" 等内容,相关的右侧将有一个文本面板、一个下拉列表和一个日历小部件.)

我 运行 遇到的问题是我希望它们水平排列(一个标签及其关联 "control areas" 应该从同一行开始)和垂直排列(所有标签应该从同一条垂直线开始,所有控制区域应该从同一条垂直线开始。)不过,有些控制区域比其他区域大或小;许多只包含一个文本面板,而其他的则有诸如单选按钮、日历小部件等与之关联的东西。

此外,所需的字段类型经常变化,并且根据上下文(例如是创建新用户还是编辑旧用户,以及控制用户的权限级别),其中一些字段可能被排除在外。所以,我想要的东西在很大程度上会自行安排 - 如果我必须非常手动地设置位置,我希望事情会因为部分被排除在外而中断。

我想看到的是一个布局管理器,基本上可以让我在灵活的网格上设置坐标,然后自动调整网格的大小以向每个元素提供最小值 space,同时仍然保持网格线笔直。这样我就可以简单地保留一个控件列表及其相关标签,并以编程方式构建面板,以便 labels/controls 始终分配给相同的行。

虽然我还没有弄清楚如何使用任何布局管理器来做到这一点:GridLayout 似乎要求所有内容都具有相同的大小,并且 BoxLayout 想要垂直排列或水平但不是两者。因此,我一直发现我的行排列但没有垂直对齐,或者错误的标签与错误的控件对齐。

有什么建议吗?

GridBagLayout 是实现此目的的最简单方法。它不是最直观的布局管理器:您需要在添加时为每个组件指定一个 'constraint'。有一些方法可以指定相对于前一个组件的位置,但我倾向于使用明确的 gridxgridy 值来确保您确切知道事情的去向。您可以有一个方法来添加标签和组件并移动到下一行,以便包含此逻辑:

private final JPanel pane = new JPanel(new GridBagLayout());
private final GridBagConstraints c = new GridBagConstraints();

private void addLabelledComponent(String label, Component component) {
    c.gridx = 0;
    pane.add(new JLabel(label), c);
    c.gridx = 1;
    pane.add(component, c);
    c.gridy++;
}

有关更多详细信息,例如定义对齐方式、填充、跨行和跨列等,请参阅 How To Use GridBagLayout

这是一个示例,如您所见,有 2 个正常大小的行、1 个中等大小的行和 1 个较大(较高)的行,顺序不同,我认为这与您想要做的类似

行高由GridBagConstraintsipady属性决定,你可以在How to use GridBagLayout and the docs

上阅读更多内容

我没怎么用过GridBagLayout,所以可能有些地方可以跳过或改进,但为了有这个例子,下面是代码的输出:

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.JTextField;
import javax.swing.SwingUtilities;

public class GridBagLayoutExample {
    private JPanel contentPane;
    private JFrame frame;
    private JLabel label;
    private JTextField field;

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new GridBagLayoutExample().createAndShowGui();
            }
        });
    }

    public void createAndShowGui() {
        frame = new JFrame("GridBagLayout Example");
        contentPane = new JPanel();
        contentPane.setLayout(new GridBagLayout());

        GridBagConstraints c = new GridBagConstraints();

        label = new JLabel("A small label");
        field = new JTextField();

        c.insets = new Insets(0, 10, 0, 10);
        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0;
        c.gridx = 0;
        c.gridy = 0;
        contentPane.add(label, c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.5;
        c.gridx = 2;
        c.gridy = 0;
        c.gridwidth = 2;
        contentPane.add(field, c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.5;
        c.gridx = 0;
        c.gridy = 1;
        c.ipady = 20;
        contentPane.add(new JLabel("Another but much larger JLabel"), c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.5;
        c.gridx = 2;
        c.gridy = 1;
        c.ipady = 20;
        c.gridwidth = 2;
        contentPane.add(new JButton("And including a larger JButton"), c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.5;
        c.gridx = 0;
        c.gridy = 2;
        c.ipady = 60;
        contentPane.add(new JLabel("A TALLER ROW!"), c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.5;
        c.gridx = 2;
        c.gridy = 2;
        c.ipady = 60;
        c.gridwidth = 2;
        contentPane.add(new JTextField(), c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.5;
        c.gridx = 0;
        c.gridy = 3;
        c.ipady = 0;
        contentPane.add(new JLabel("Another normal row"), c);

        c.fill = GridBagConstraints.HORIZONTAL;
        c.weightx = 0.5;
        c.gridx = 2;
        c.gridy = 3;
        c.gridwidth = 2;
        contentPane.add(new JTextField(), c);

        frame.add(contentPane);
        frame.pack();
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
}

我希望你现在心里有一个想法,如果你还有疑问,问另一个(更具体的)问题,包括你的 mcve