【问题标题】:GridBagLayout anchor precisenessGridBagLayout 锚点精确度
【发布时间】:2022-01-08 00:09:40
【问题描述】:

我可以消除容器周围的随机红线还是这是 JDK 中的错误?
3x3 网格使用enchor 约束添加了 9 个尺寸更小的组件,但未按预期工作。

重现问题的完整源代码

import static java.awt.Color.red;
import static java.awt.Color.white;
import static java.awt.GridBagConstraints.CENTER;
import static java.awt.GridBagConstraints.FIRST_LINE_END;
import static java.awt.GridBagConstraints.FIRST_LINE_START;
import static java.awt.GridBagConstraints.LAST_LINE_END;
import static java.awt.GridBagConstraints.LAST_LINE_START;
import static java.awt.GridBagConstraints.LINE_END;
import static java.awt.GridBagConstraints.LINE_START;
import static java.awt.GridBagConstraints.PAGE_END;
import static java.awt.GridBagConstraints.PAGE_START;
import static javax.swing.BorderFactory.createLineBorder;
import static javax.swing.JFrame.EXIT_ON_CLOSE;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

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

public class GridBagLayoutTest {
    
    public static void main(String[] args) {
        JPanel pane = new JPanel(new GridBagLayout());
        pane.setBackground(red);
        pane.setPreferredSize(new Dimension(500, 500));
        // pane.setBorder(createEmptyBorder());
        // pane.setBorder(createLineBorder(white, 1));
        pane.setBorder(createLineBorder(white, 2));
        // pane.setBorder(createLineBorder(white, 3));
        
        GridBagConstraints c = new GridBagConstraints();
        c.weightx = 1;
        c.weighty = 1;
        c.gridx = 0;
        c.gridy = 0;
        c.anchor = FIRST_LINE_START;
        pane.add(rectangle(), c);
        c.gridx = 1;
        c.gridy = 0;
        c.anchor = PAGE_START;
        pane.add(rectangle(), c);
        c.gridx = 2;
        c.gridy = 0;
        c.anchor = FIRST_LINE_END;
        pane.add(rectangle(), c);
        c.gridx = 0;
        c.gridy = 1;
        c.anchor = LINE_START;
        pane.add(rectangle(), c);
        c.gridx = 1;
        c.gridy = 1;
        c.anchor = CENTER;
        pane.add(rectangle(), c);
        c.gridx = 2;
        c.gridy = 1;
        c.anchor = LINE_END;
        pane.add(rectangle(), c);
        c.gridx = 0;
        c.gridy = 2;
        c.anchor = LAST_LINE_START;
        pane.add(rectangle(), c);
        c.gridx = 1;
        c.gridy = 2;
        c.anchor = PAGE_END;
        pane.add(rectangle(), c);
        c.gridx = 2;
        c.gridy = 2;
        c.anchor = LAST_LINE_END;
        pane.add(rectangle(), c);
        
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(600, 600));
        frame.getContentPane().setLayout(new GridBagLayout());
        frame.getContentPane().setBackground(white);
        frame.getContentPane().add(pane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    
    public static JPanel rectangle() {
        JPanel rect = new JPanel();
        rect.setBackground(white);
        rect.setPreferredSize(new Dimension(100, 100));
        return rect;
    }
}

【问题讨论】:

  • 我无法使用GridBagLayout 让它工作。你会改用GridLayout 解决吗?
  • 顺便说一句,这样做的目的是什么?例如。它是棋盘游戏中的 5x5 网格吗?
  • 感谢您的回答!这不是 5x5 网格,而是 3x3 网格,其中 9 个单元格填充了 9 个较小的矩形,并添加了 anchor 约束,这无法按预期工作。该示例是合成的,以明确舍入问题。我正在我的应用程序中构建左侧菜单面板,发现我的“左下菜单按钮”不能很好地贴在左下角...
  • 如果您从官方布局教程页面运行 jnlp example,您会发现在调整窗口大小时,按钮会随机粘在或不粘在外窗口上。这太可怕了:(
  • @Mike 好吧,如果课程没有按照您想要的方式工作,我们无能为力。在您的问题中,您没有提到您认为问题是舍入错误。我花时间向您证明这是一个舍入问题,因此无法使用 GridBagLayout 解决该问题。然后我提出了多个建议,允许您使用不同的方法或布局管理器来解决问题。所以是的,答案/cmets 中提供的信息确实回答了这个问题,可能对其他人有帮助。回复时不要忘记“@...”标签。

标签: java swing layout-manager gridbaglayout


【解决方案1】:

这是 JDK 中的错误吗?

嗯,您正在尝试以一种并非设计用于使用的方式来使用 GridBagLayout。您正在尝试使用每行/列中的 3 个组件创建一个 5 个组件的网格。

每个单元格的大小为 500 / 3 = 166.666。

当您尝试设置每个组件的位置时,您会遇到一些舍入问题。有时向上取整,有时向下取整。

要检查这一点,您可以去掉 setBorder(...) 语句,然后在 setVisible() 语句之后添加:

for (Component panel: pane.getComponents())
    System.out.println(panel.getBounds());

你会看到如下输出:

java.awt.Rectangle[x=1,y=1,width=100,height=100]
java.awt.Rectangle[x=200,y=1,width=100,height=100]
java.awt.Rectangle[x=399,y=1,width=100,height=100]
java.awt.Rectangle[x=1,y=200,width=100,height=100]
java.awt.Rectangle[x=200,y=200,width=100,height=100]
java.awt.Rectangle[x=399,y=200,width=100,height=100]
java.awt.Rectangle[x=1,y=399,width=100,height=100]
java.awt.Rectangle[x=200,y=399,width=100,height=100]
java.awt.Rectangle[x=399,y=399,width=100,height=100]

要解决此问题,您可以创建一个 5x5 网格并将每个组件添加到网格中的相应单元格:

import static java.awt.Color.red;
import static java.awt.Color.white;
import static java.awt.GridBagConstraints.CENTER;
import static java.awt.GridBagConstraints.FIRST_LINE_END;
import static java.awt.GridBagConstraints.FIRST_LINE_START;
import static java.awt.GridBagConstraints.LAST_LINE_END;
import static java.awt.GridBagConstraints.LAST_LINE_START;
import static java.awt.GridBagConstraints.LINE_END;
import static java.awt.GridBagConstraints.LINE_START;
import static java.awt.GridBagConstraints.PAGE_END;
import static java.awt.GridBagConstraints.PAGE_START;
import static javax.swing.BorderFactory.createLineBorder;
import static javax.swing.JFrame.EXIT_ON_CLOSE;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

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

import java.util.Arrays;

public class GridBagLayoutTest2 {

    public static void main(String[] args) {
        GridBagLayout gbl = new GridBagLayout();
        JPanel pane = new JPanel(gbl);
        pane.setBackground(red);
//        pane.setPreferredSize(new Dimension(500, 500));
        // pane.setBorder(createEmptyBorder());
        // pane.setBorder(createLineBorder(white, 1));
        pane.setBorder(createLineBorder(white, 2));
        // pane.setBorder(createLineBorder(white, 3));


        //  The minimimum width of a column is 100 pixels
        //  and the minimum height of a row is 100 pixels.

        int[] columns = new int[5];
        Arrays.fill(columns, 100);
        gbl.columnWidths = columns;

        int[] rows = new int[5];
        Arrays.fill(rows, 100);
        gbl.rowHeights = rows;

        GridBagConstraints c = new GridBagConstraints();
        c.weightx = 1;
        c.weighty = 1;
        c.gridx = 0;
        c.gridy = 0;
//        c.anchor = FIRST_LINE_START;
        pane.add(rectangle(), c);
        c.gridx = 2;
        c.gridy = 0;
//        c.anchor = PAGE_START;
        pane.add(rectangle(), c);
        c.gridx = 4;
        c.gridy = 0;
//        c.anchor = FIRST_LINE_END;
        pane.add(rectangle(), c);
        c.gridx = 0;
        c.gridy = 2;
//        c.anchor = LINE_START;
        pane.add(rectangle(), c);
        c.gridx = 2;
        c.gridy = 2;
//        c.anchor = CENTER;
        pane.add(rectangle(), c);
        c.gridx = 4;
        c.gridy = 2;
//        c.anchor = LINE_END;
        pane.add(rectangle(), c);
        c.gridx = 0;
        c.gridy = 4;
//        c.anchor = LAST_LINE_START;
        pane.add(rectangle(), c);
        c.gridx = 2;
        c.gridy = 4;
//        c.anchor = PAGE_END;
        pane.add(rectangle(), c);
        c.gridx = 4;
        c.gridy = 4;
//        c.anchor = LAST_LINE_END;
        pane.add(rectangle(), c);

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(600, 600));
        frame.getContentPane().setLayout(new GridBagLayout());
        frame.getContentPane().setBackground(white);
        frame.getContentPane().add(pane);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static JPanel rectangle() {
        JPanel rect = new JPanel();
        rect.setBackground(white);
        rect.setPreferredSize(new Dimension(100, 100));
        return rect;
    }
}

有关此功能的原因,请参阅:Creating a board game layout using JLayeredPane

编辑:

GridLayout 也有同样的问题。如果父面板的宽度/高度不能被 GridLayout 中的组件数整除,则组件不会填满整个空间,父面板的背景会透出来。

作为您提供的代码的 hack,您可以使用:

Border line = new LineBorder(Color.WHITE);
Border empty = new EmptyBorder(-1, -1, -1, -1);
Border compound = new CompoundBorder(line, empty);
pane.setBorder(compound);

这将允许绘制 LineBorder 而不会影响面板中组件的位置,因为边框插入将保持 (0, 0, 0, 0)。

您还应该能够使用 MatteBorder 并且只绘制底线并相应地调整 EmptyBorder。

【讨论】:

  • 对不起,我没有明白你所说的关于 5 组件网格的意思。我使用的是 3x3 网格,所有 9 个矩形都“小于其显示区域”,分别放置在 anchor 设计的相应(正确)单元格中。我面临的问题确实是舍入问题,我认为这是一个错误,因为如果我将组件放在“左下角”,我不希望在组件的左侧或下方出现任何空间。这对我来说很关键,因为它不能很好地粘在其他容器的外部组件上。
  • 这个例子是合成的,使四舍五入的问题更加生动。在我的应用程序中,我正在构建序号左侧菜单面板,我发现我的“左下按钮”实际上并没有很好地贴在左下角。当我调整窗口大小时,它正在“跳跃”!这不是我对“最先进的布局管理器”和经过 20 年测试的库所期望的......
  • 500 / 3 = 166.666 这是我所期望的:两个大小为 167 的单元格和一个大小为 166 的单元格。LAST_LINE_START 应该很好地贴在左下角,内部没有任何舍入的间歇空间。间歇性舍入空间应位于该单元格的顶部和右侧
  • 我希望:... - 我不知道有任何文档表明额外的像素将分配给任何特定列。如果您希望组件在右下角对齐,请尝试使用 BoxLayout。
  • 这是重点,它没有对齐。它应该在左下角,但组件下方有一个空格
猜你喜欢
  • 2014-09-26
  • 2011-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多