GridBagLayout 锚点精确性

GridBagLayout anchor preciseness

我可以消除容器周围的随机红线吗?或者这是 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;
    }
}

this is a bug in JDK?

好吧,您正在尝试以一种非设计用途的方式使用 GridBagLayout。您正在尝试在每个 row/column.

中使用 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;
    }
}

这个有效的原因见:

编辑:

GridLayout 也有同样的问题。如果父面板的 width/height 不能被 GridLayout 中组件的数量整除,那么组件将不会填满整个 space 并且父面板的背景会透出来。

作为对您提供的代码的破解,您可以使用:

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。