GridBagLayout 为列提供额外的边距

GridBagLayout gives extra margin to column

我使用 Swing 已经有一段时间了,我终于找到了学习 GridBadLayout 的自信。

我还在学习,在这种情况下我不明白为什么下面的代码没有按预期响应:特别是我不明白为什么布局以这种方式显示列。

通过 运行 代码片段,您会发现代表意大利国旗的面板位置不正确:最后一列(国旗的红色部分)与国旗的其余部分(白色部分)分离旗帜)。那么,我做错了什么,我可以解决什么问题才能正确表示标志?

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class test extends JPanel {

    private GridBagConstraints gbc;
    private final int CELL_WIDTH = 90;
    private final int CELL_HEIGHT = 110;

    public test() {
        setLayout(new GridBagLayout());

        putBanner(0);

        putFlagRow(1);
        putFlagRow(2);
        putFlagRow(3);
        putFlagRow(4);
    }

    public JPanel getUserPanel(Color c) {
        JPanel ol = new JPanel();
        ol.setPreferredSize(new Dimension(CELL_WIDTH * 10, CELL_HEIGHT));
        ol.setBackground(c);
        return ol;
    }

    public JPanel gettestPanel() {
        JPanel ol = new JPanel();
        ol.setPreferredSize(new Dimension(CELL_WIDTH, CELL_HEIGHT));
        ol.setBackground(Color.white);
        return ol;
    }

    private void putFlagRow(int gridy) {
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = gridy;
        JPanel p1=gettestPanel();
        p1.setBackground(Color.green);
        add(p1, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 3;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 4;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 5;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 6;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 7;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 8;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 9;
        gbc.gridy = gridy;
        JPanel p=gettestPanel();
        p.setBackground(Color.red);
        add(p, gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 10;
        gbc.gridy = gridy;
        add(gettestPanel(), gbc);
    }

    private void putBanner(int gridy) {
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = gridy;
        gbc.gridwidth = 1;
        add(gettestPanel(), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = gridy;
        gbc.gridwidth = 9;
        add(getUserPanel(Color.black), gbc);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("GridBagLayoutTest");
        frame.add(new test());
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

除了了解这个特定问题及其原因外,我还想了解这一点,它直接来自 GridBagLayout Oracle 文档:

weightx, weighty Specifying weights is an art that can have a significant impact on the appearance of the components a GridBagLayout controls. Weights are used to determine how to distribute space among columns (weightx) and among rows (weighty); this is important for specifying resizing behavior. Unless you specify at least one non-zero value for weightx or weighty, all the components clump together in the center of their container. This is because when the weight is 0.0 (the default), the GridBagLayout puts any extra space between its grid of cells and the edges of the container. Generally weights are specified with 0.0 and 1.0 as the extremes: the numbers in between are used as necessary. Larger numbers indicate that the component's row or column should get more space. For each column, the weight is related to the highest weightx specified for a component within that column, with each multicolumn component's weight being split somehow between the columns the component is in. Similarly, each row's weight is related to the highest weighty specified for a component within that row. Extra space tends to go toward the rightmost column and bottom row.

类似

的表达

is an art

the numbers in between are used as necessary

确实给我的印象是,即使是文档作者也不认为可以教授这种约束的用法。那可不行

weightX 和 weighty 很容易使用,但教程没有帮助......假设当你制作一个对话框并且你 pack() 它时,组件有一个位置和大小以及一切可以(或不可以)。如果调整对话框大小并使其变大会发生什么情况?突然有更多 space 并且组件必须增长,这就是 Swing 查看 weight* 参数的时刻。

如果一个组件有 weightx=1.0 而其余的 weightx=0.0 意味着第一个组件获得所有新的 space 而第二个保持其大小。如果组件有 weightx=0.5 表示两个组件共享新大小,各占 50%。如果组件的 weightx 联合值大于 1.0,则意味着组件需要超过新 space 的 100%,那么...好吧,神奇的东西...

假设您正在使用网络浏览器。您希望带有选项卡、按钮和菜单的顶部面板始终保持相同大小,而网页的中心面板获得所有新的额外大小,因此您给中心面板 weight*=1.0weight*=0.0 用于顶部面板。

尽量不要在任何行或列中分配超过 1.0 的权重,一切都会好的。

the last column(red part of the flag) is detached from the rest of the flag(white part of the flag).

调试 GridBagLayout 时要做的一件事是向组件添加 Border,这样您就可以看到每个单元格的大小。我将以下内容添加到您的 getTestPanel() 方法中:

ol.setBorder( new LineBorder(Color.BLUE) );

您可以看到面板大小合适,但面板周围有多余的 space。

然后你看看你的用户面板:

ol.setPreferredSize(new Dimension(CELL_WIDTH * 10, CELL_HEIGHT));

即使您只将网格宽度设置为 9 个单元格,您也会看到大小设置为 10 个单元格。所以现在这 9 个单元格必须适合用户面板的 space,看起来额外的 space 被赋予了 9 列的最后一列。

代码应该是:

//ol.setPreferredSize(new Dimension(CELL_WIDTH * 10, CELL_HEIGHT));
ol.setPreferredSize(new Dimension(CELL_WIDTH * 9, CELL_HEIGHT));

另一种选择是不为用户面板提供首选大小,而是使用 "fill" 约束,这样用户面板现在将占据与九列相同的 space。

weightx, weighty Specifying weights is an art...

这与您的问题无关。要查看此效果,请将框架拖动得更大。现在整个面板都将位于框架的中心,因为组件的 none 使用 weightx/weighty 约束。这意味着 none 的组件将增长以填充空 space if frame.

如果其中一个组件的 weightx 约束为 1.0,则该列将在框架调整大小时扩展并填充额外的 space。

我想你想要达到的目标是这样的:

import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Test extends JPanel {

    private static final long serialVersionUID = 1L;

    public Test() {
        setLayout(new GridBagLayout());
        putBanner(0);
        putFlagRow(1);
    }

    public JPanel getUserPanel(Color c) {
        JPanel panel = new JPanel();
        panel.setBackground(c);
        return panel;
    }

    private void putFlagRow(int gridy) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = gridy;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 0.5;
        gbc.weighty = 1;
        add(getUserPanel(Color.green), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = gridy;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 1;
        gbc.weighty = 1;
        add(getUserPanel(Color.white), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = gridy;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 0.5;
        gbc.weighty = 1;
        add(getUserPanel(Color.red), gbc);
    }

    private void putBanner(int gridy) {
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = gridy;
        gbc.ipady = 50;
        gbc.fill = GridBagConstraints.BOTH;
        add(getUserPanel(Color.white), gbc);

        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = gridy;
        gbc.gridwidth = 2;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.weightx = 1;
        add(getUserPanel(Color.black), gbc);
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("GridBagLayoutTest");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.setContentPane(new Test());
        frame.setSize(450, 300);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}

GridBagLayout 是一种非常复杂且功能强大的内容对齐方式,用两句话来解释它并不容易,所以我认为最简单的方法是尝试或观看一些教程,但我'我试试看:
您必须像编辑 Excel table 一样思考。如果您调整一个单元格的宽度,则整列将具有此宽度,与 rows/height 相同。所以现在你不必添加白色平面的色调,你只需要一个并适当调整它的大小。
接下来就是不要设置固定大小,布局会为您完成,这就是它们的用途或使用绝对坐标(无布局:setLayout(null))。
所以现在你必须告诉 GridBagLayout 如何对齐组件。不同的对齐方式有不同的变量。首先(您应该已经知道)gridxgridy 保持组件的位置(在 Excel 中类似于 A1 或 C12)。比你可以 'combine cells' 与 gridwidthgridheight.
所以现在您的组件已定位,但它不会随着 window 的大小而改变其大小。直到知道单元格只有其组件的大小getPreferredSize()weightxweighty 是下一个要查看的变量。这些定义了应该使用多少剩余的space。例如,您有两个组件(水平并排),它们的首选宽度总和为 300px,但 window 的宽度为 1000px,不会使用 700px。因此,如果您现在将 weightx 都设置为 1,它们会尝试使用 100% 的剩余像素,但它们不能同时拥有 700px,因此每个人都会收到 350px。如果现在将一个 weightx 设置为 0,将另一个设置为 1,则第一个不会获得额外的像素,而另一个则全部为 700px。
如果您到目前为止都尝试过,您会看到面板之间有很多空白 space,那是因为只有单元格被调整大小,而不是它们的内容。现在您可以设置组件是否应该显示在带有 anchor 的单元格中(例如 GridBagConstraints.NORTH/SOUTH/SOUTHEAST),但是您仍然有那些空的 spaces 并且没有标志为空 space,您要查找的是 fill.
fill 让您选择如何处理单元格中的空 space。使用 GridBagConstraints.NONE 组件不会调整大小,HORIZONTAL 仅将宽度调整为单元格的宽度(VERTICAL 仅调整高度)并且 BOTH 调整组件大小以适应单元格大小。
变量 insets 添加空插入,例如您选择将 fill 设置为 BOTH,但仍希望组件上方有空 space(插入是固定的,如果你想要一个动态插图,你必须通过添加具有特定 GridBagConstraints 的空组件来对齐你想要显示的插图来解决。
ipadxipady 是要添加到单元格大小的固定值,例如 ipadx 设置为 5,单元格的宽度将至少为组件的最小宽度加上ipadx 的 5px。换句话说,单元格的宽度将始终比组件的宽度至少大 5px(如果 fill 设置为 BOTHHORIZONTAL 组件随后将调整为新的宽度).