GridBagLayout:元素在非常特定的面板高度收缩

GridBagLayout: Elements shrink at a very specific panel height

我正在使用 GridBagLayout 创建一个包含 4 个子面板的面板。 4 个子面板为洋红色、蓝色、红色和青色,而主面板为绿色。目标是以某种方式排列这 4 个子面板,理论上,不应显示任何绿色。幸运的是,我有一个确切说明我想要实现的工作示例:

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

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

public class Main {

    public static void main(String[] args) {

        Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
        int width = d.width;
        int height = 1046;


        JFrame frame = new JFrame("4.5 test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JPanel masterPanel = new JPanel(new GridBagLayout());
        masterPanel.setBackground(Color.GREEN);

        GridBagConstraints c = new GridBagConstraints();
        c.weightx = 1.0;
        c.weighty = 1.0;

    // 1
        JPanel leftUpper = new JPanel();
        leftUpper.setBackground(Color.MAGENTA);
        leftUpper.setPreferredSize(new Dimension(width/3, height/7));
        c.gridx = 0;
        c.gridy = 0;
        c.anchor = GridBagConstraints.NORTH;
        masterPanel.add(leftUpper, c);


    // 2
        JPanel leftLower = new JPanel();
        leftLower.setBackground(Color.BLUE);
        leftLower.setPreferredSize(new Dimension(width/3, height - height/7));
        c.gridheight = 2;
        c.anchor = GridBagConstraints.SOUTH;
        masterPanel.add(leftLower, c);

    // 3
        JPanel rightUpper = new JPanel();
        rightUpper.setBackground(Color.RED);
        rightUpper.setPreferredSize(new Dimension(width - width/3, height - height/5));
        c.gridx = 1;
        c.gridheight = 1;
        masterPanel.add(rightUpper, c);


    // 4
        JPanel rightLower = new JPanel();
        rightLower.setBackground(Color.CYAN);
        rightLower.setPreferredSize(new Dimension(width - width/3, height/5));
        c.gridy = 1;
        masterPanel.add(rightLower, c);



        frame.add(masterPanel);
        frame.pack();
        frame.setVisible(true);
    }

}

当运行时,这给出 this correct result. 事实上,1046 以下的每个高度都按预期工作。

但是,时刻将第 16 行更改为:

int height = 1047;

见鬼去吧,我们得到 this instead. 这也适用于 1047 以上的所有高度。面板位于正确的位置,但与正确的尺寸相去甚远。请注意,与高度不同,更改宽度没有(意外)效果。

如果这种行为不是由如此小的(任意的?)大小限制引起的,我不会对这种行为感到惊讶。对于上下文,我使用的是 1.8 Java RunTime 环境。我的电脑屏幕分辨率是 1920x1080。为什么会发生这种情况,如何补救,如果这是一种残暴的做法,您可以提供什么详细的替代方案?

GridBagLayout 当没有足够的空间将组件的大小至少调整到它们的首选大小时,会出现奇怪的行为。它不会优雅地降级。

看来您正在尝试绝对布局。 GridBagLayout 和其他通用 LayoutManger(其他 X-Y 布局管理器)在这些情况下效果不佳。

坚持使用 GridBagLayout 通常最好在 JScrollPane 或至少 JPanel 中使用它,最小尺寸取自其首选尺寸。

当 GridBagLayout 无法容纳所有子组件的首选尺寸时,它会“放弃”并将每个子组件设置为其最小尺寸。因此,一种解决方案是将每个组件的最小尺寸设置为其首选尺寸。

但是,与其尝试从数学上解释屏幕上的每个像素,您还有更可靠的选择。

一个解决方案是只设置一个面板的大小,在任何特定维度上,所以另一个总是占据剩余的space。这样,您就永远不会“溢出”可用 space。 BorderLayout 非常适合这种情况,因为它的中心组件被拉伸以占用所有可用的 space 侧面组件不使用。

因此,对于洋红色和蓝色面板,您只需设置洋红色面板的高度:

JPanel leftPanel = new JPanel(new BorderLayout());

// 1
    JPanel leftUpper = new JPanel();
    leftUpper.setBackground(Color.MAGENTA);
    leftUpper.setPreferredSize(new Dimension(1, height/7));
    leftPanel.add(leftUpper, BorderLayout.NORTH);

// 2
    JPanel leftLower = new JPanel();
    leftLower.setBackground(Color.BLUE);
    leftPanel.add(leftLower, BorderLayout.CENTER);

红色和青色的也类似:

JPanel rightPanel = new JPanel(new BorderLayout());

// 3
    JPanel rightUpper = new JPanel();
    rightUpper.setBackground(Color.RED);
    rightPanel.add(rightUpper, BorderLayout.CENTER);


// 4
    JPanel rightLower = new JPanel();
    rightLower.setBackground(Color.CYAN);
    rightLower.setPreferredSize(new Dimension(1, height/5));
    rightPanel.add(rightLower, BorderLayout.SOUTH);

然后你做一些类似于这两个面板本身的事情,把它们放在一起:

    JPanel masterPanel = new JPanel(new BorderLayout());
    masterPanel.setBackground(Color.GREEN);

    leftPanel.setPreferredSize(new Dimension(width / 3,
        leftPanel.getPreferredSize().height));

    masterPanel.add(leftPanel, BorderLayout.WEST);
    masterPanel.add(rightPanel, BorderLayout.CENTER);

并且由于系统的 window 管理器报告给 windows 的边界是不可靠的,因此您避免进行任何显式数学运算而只是最大化 window:

    frame.add(masterPanel);
    frame.pack();
    frame.setExtendedState(Frame.MAXIMIZED_BOTH);
    frame.setVisible(true);

如果您绝对需要保持面板之间的比例(例如,magenta_height : blue_height :: 1 : 6),您可以使用 SpringLayout,但 SpringLayout 更复杂,通常不值得麻烦。它允许您指定可以是其他尺寸范围的倍数的尺寸范围:

    SpringLayout layout = new SpringLayout();

    JPanel masterPanel = new JPanel(layout);
    masterPanel.setBackground(Color.GREEN);

// 1
    JPanel leftUpper = new JPanel();
    leftUpper.setBackground(Color.MAGENTA);
    masterPanel.add(leftUpper);

// 2
    JPanel leftLower = new JPanel();
    leftLower.setBackground(Color.BLUE);
    masterPanel.add(leftLower);

// 3
    JPanel rightUpper = new JPanel();
    rightUpper.setBackground(Color.RED);
    masterPanel.add(rightUpper);

// 4
    JPanel rightLower = new JPanel();
    rightLower.setBackground(Color.CYAN);
    masterPanel.add(rightLower);

    Spring leftUpperHeight = Spring.height(leftUpper);
    Spring leftLowerHeight = Spring.scale(leftUpperHeight, 6);

    Spring rightLowerHeight = Spring.scale(leftUpperHeight, 7 / 5f);

    Spring leftWidth = Spring.width(leftUpper);
    Spring rightWidth = Spring.scale(leftWidth, 3);

    layout.getConstraints(leftLower).setHeight(leftLowerHeight);
    layout.getConstraints(rightLower).setHeight(rightLowerHeight);

    layout.getConstraints(rightUpper).setWidth(rightWidth);
    layout.getConstraints(rightLower).setWidth(rightWidth);

    // Place leftLower beneath leftUpper.
    layout.putConstraint(
        SpringLayout.NORTH, leftLower, 0,
        SpringLayout.SOUTH, leftUpper);

    // Make leftLower's width match leftUpper's width.
    layout.putConstraint(
        SpringLayout.WEST, leftLower, 0,
        SpringLayout.WEST, leftUpper);
    layout.putConstraint(
        SpringLayout.EAST, leftLower, 0,
        SpringLayout.EAST, leftUpper);

    // Make container high enough to hold both leftLower and leftUpper.
    layout.putConstraint(
        SpringLayout.SOUTH, masterPanel, 0,
        SpringLayout.SOUTH, leftLower);

    // Place rightUpper and rightLower to the right of leftUpper.
    layout.putConstraint(
        SpringLayout.WEST, rightLower, 0,
        SpringLayout.EAST, leftUpper);
    layout.putConstraint(
        SpringLayout.WEST, rightUpper, 0,
        SpringLayout.WEST, rightLower);

    // Make container wide enough to accommodate rightUpper and rightLower.
    layout.putConstraint(
        SpringLayout.EAST, masterPanel, 0,
        SpringLayout.EAST, rightLower);

    // Align bottom of rightLower with bottom of leftLower.
    layout.putConstraint(
        SpringLayout.SOUTH, rightLower, 0,
        SpringLayout.SOUTH, leftLower);

    // Place rightUpper above rightLower.
    layout.putConstraint(
        SpringLayout.SOUTH, rightUpper, 0,
        SpringLayout.NORTH, rightLower);

    // Stretch rightUpper to reach to the top of the container.
    layout.putConstraint(
        SpringLayout.NORTH, rightUpper, 0,
        SpringLayout.NORTH, masterPanel);