【问题标题】:GridBagLayout gives extra margin to columnGridBagLayout 为列提供额外的边距
【发布时间】:2018-08-08 11:18:28
【问题描述】:

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

我仍在学习它,在这种情况下,我无法理解为什么以下代码没有按预期响应:特别是我无法理解为什么布局会以这种方式显示列。

通过运行 sn-p,您将发现代表意大利国旗的面板位置不正确:最后一列(国旗的红色部分)与国旗的其余部分(国旗的白色部分)分离.那么,我做错了什么,我可以修复什么来代表正确的标志?

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,有重量 指定权重是一门艺术,它可以对 GridBagLayout 控件的组件的外观产生重大影响。重量是 用于确定如何在列之间分配空间 (weightx) 和 行间(重量级);这对于指定调整大小很重要 行为。除非您为 weightx 指定至少一个非零值 或沉重,所有组件都聚集在它们的中心 容器。这是因为当权重为 0.0(默认值)时, GridBagLayout 在其单元格网格和 容器的边缘。通常权重指定为 0.0 和 1.0 作为极端:根据需要使用中间的数字。较大的数字表示组件的行或列应该得到 更多空间。对于每一列,权重与最高的相关 为该列中的组件指定的 weightx,每个 多列组件的权重在列之间以某种方式分配 组件在其中。类似地,每一行的权重都与 为该行中的组件指定的最高权重。额外空间 倾向于走向最右边的列和底行。

这样的表达

是一门艺术

根据需要使用中间的数字

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

【问题讨论】:

    标签: java swing layout-manager gridbaglayout


    【解决方案1】:

    最后一列(标志的红色部分)与标志的其余部分(标志的白色部分)分离。

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

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

    您可以看到面板大小合适,但面板周围有额外的空间。

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

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

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

    代码应该是:

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

    另一个选项是不为用户面板提供首选大小,但随后使用“填充”约束,这样用户面板现在将占用与九列相同的空间。

    weightx, weighty 指定权重是一门艺术...

    这与您的问题无关。要查看此效果,请将框架拖得更大。现在整个面板都将位于框架的中心,因为您的组件都没有使用 weightx/weighty 约束。这意味着如果框架,任何组件都不会增长以填充空白空间。

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

    【讨论】:

    • 天哪,现在我看到了这个问题。顺便说一句,我的布局方法正确吗?基本上我所理解的是,为了“随心所欲地玩”我的网格,我必须建立一个地面尺寸(CELL_WIDTH,CELL_HEIGHT)。也就是说,实现 colspans 和 rowspans 的问题是:[1] 根据需要放大组件的实际大小(即 CELL_WIDTH*3)[2] 设置一致的网格宽度/网格高度
    【解决方案2】:

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

    如果一个组件有weightx=1.0 而其余的weightx=0.0 意味着第一个组件获得所有新空间,第二个保持其大小。如果组件有weightx=0.5 表示这两个组件共享新的大小,各占50%。如果组件的 weightx 值大于 1.0,则意味着组件需要超过 100% 的新空间,所以……嗯,有点神奇……

    想象一下你正在做一个网络浏览器。您希望带有选项卡、按钮和菜单的顶部面板始终保持相同的大小,而网页的中心面板获得所有新的额外大小,因此您将中心面板提供给顶部的 weight*=1.0weight*=0.0面板。

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

    【讨论】:

      【解决方案3】:

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

      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 表格一样思考。如果您调整一个单元格的宽度,整列将具有此宽度,与行/高度相同。所以现在您不必添加白色平面的色调,您只需要一个并适当地调整它的大小。
      接下来的事情是不要设置固定大小,布局会为你做这件事,这就是它们的用途或使用绝对坐标(无布局:setLayout(null))。
      所以现在你必须告诉 GridBagLayout 如何对齐组件。不同的对齐方式有不同的变量。首先(您应该已经知道)gridxgridy 保持组件的位置(在 Excel 中,例如 A1 或 C12)。您可以将单元格与gridwidthgridheight“组合”。
      因此,现在您的组件已定位,但它不会随着窗口的大小而改变其大小。直到知道单元格只有其组件getPreferredSize() 的大小。 weightxweighty 是下一个要查看的变量。这些定义了应该使用多少剩余空间。例如,您有两个组件(水平并排),它们的首选宽度总和为 300 像素,但窗口的宽度为 1000 像素,不会使用 700 像素。因此,如果您现在将weightx 都设置为 1,他们会尝试使用 100% 的剩余像素,但他们不能同时拥有 700px,所以每个人都会收到 350px。如果您现在将一个 weightx 设置为 0,另一个设置为 1,则第一个没有额外的像素,而另一个全部为 700 像素。
      如果您尝试到现在,您会看到面板之间有很多空白,那是因为只有单元格被调整大小,而不是它们的内容。现在您可以设置组件是否应该在其单元格中显示为anchor(例如GridBagConstraints.NORTH/SOUTH/SOUTHEAST),但是您仍然有那些空白并且没有标志有空白,什么您正在寻找的是fill
      fill 让您选择,如何处理单元格中的空白空间。使用GridBagConstraints.NONE,组件不会调整大小,HORIZONTAL 仅将宽度调整为单元格的宽度(VERTICAL 仅调整高度),BOTH 调整组件大小以适应单元格大小。
      变量insets 增加了空插入,例如您选择将fill 设置为BOTH,但仍然希望在组件上方有空白空间(插入是固定的,如果您想要动态插入,则必须变通通过添加具有特定 GridBagConstraints 的空组件以对齐要显示的插图)。
      ipadxipady 是要添加到单元格大小的固定值,例如 ipadx 设置为 5 ,单元格的宽度至少为组件的最小宽度加上ipadx 的 5px。换句话说,单元格的宽度将始终比组件的宽度至少大 5px(如果 fill 设置为 BOTHHORIZONTAL,组件随后将调整为新的宽度)。

      【讨论】: