GridBagLayout 不稳定的大小调整和显示

GridBagLayout erratic resizing and displaying

我正在制作一个在容器中使用 GridBagLayout 的程序。我对此的主要用途是有两个 JPanel,分别占据 window 的水平 space 的 75% 和 25%。但出于某种原因,这两个面板看起来更像是 90/10,并且在调整大小时,较小的面板的尺寸会迅速变化,在它明显的最小尺寸和我认为所需的 25% 之间。

这里是相关代码。

frmReedreadV = new JFrame();
    frmReedreadV.setBounds(x, y, width, height);
    frmReedreadV.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frmReedreadV.getContentPane().setLayout(new BoxLayout(frmReedreadV.getContentPane(), BoxLayout.Y_AXIS));

    JPanel stretchyPanel = new JPanel();
    frmReedreadV.getContentPane().add(stretchyPanel);
    stretchyPanel.setLayout(new CardLayout(0, 0));

    JPanel textAndUsers = new JPanel(new GridBagLayout());

    GridBagConstraints gbc = new GridBagConstraints();
    gbc.fill = GridBagConstraints.BOTH;

    gbc.weighty = 1;

    textArea = new JTextArea();
    textArea.setMargin(new Insets(2, 5, 5, 2));
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
    textArea.setEditable(false);

    scrollPane = new JScrollPane(textArea);
    scrollPane.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));

    gbc.weightx = 0.8;
    textAndUsers.add(scrollPane, gbc);

    list = new FriendsList(listUpdate);

    gbc.weightx = 0.2;
    textAndUsers.add(list.frmUserList, gbc);

    stretchyPanel.add(textAndUsers);

FriendsList 是包含在 JPanel 中的 JList。

CardLayout 主内容窗格中还有其他按钮和文本字段,但这些应该不会影响此 GridBagLayout 中的内容,对吗?

我制作了这个 JPanel 的另一个副本作为独立应用程序,它可以完美地显示和调整大小。看这里:

JFrame frame = new JFrame();
    frame.getContentPane().setLayout((new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS)));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(100, 100, 550, 600);

    JPanel stretchyPane = new JPanel();
    frame.getContentPane().add(stretchyPane);
    stretchyPane.setLayout(new CardLayout(0, 0));

    JPanel panel = new JPanel(new GridBagLayout());
    GridBagConstraints c = new GridBagConstraints();

    JTextArea text = new JTextArea();
    text.setMargin(new Insets(2, 5, 5, 2));
    JScrollPane panel1 = new JScrollPane(text);
    FriendsList panel2 = new FriendsList(new Object());

    c.fill = GridBagConstraints.BOTH;
    c.weightx = .8;
    c.weighty = 1;
    panel.add(panel1, c);
    c.weightx = .2;
    //c.fill = GridBagConstraints.HORIZONTAL;
    panel.add(panel2.frmUserList, c);

    stretchyPane.add(panel);

    frame.setVisible(true);

是什么导致了两者之间的差异,因为我已将原始文件逐行复制到副本中?

weightx 和 weighty 属性可能看起来像比例大小,但这不是它们的作用。事实上,它们决定了额外 space 在布局中的分布。

如果您通过在 JFrame 上调用 pack() 将所有内容设置为首选大小,则不会有额外的 space。这意味着 weightx 和 weighty 属性在该状态下无效。

一旦用户开始将 window 调整得更大,就会有额外的 space,只有那时 GridBagLayout 才会参考 weightx 和 weighty 属性来确定如何分配额外的 space 到每一列和每一行。在那之前,weightx 小的组件完全有可能比 weightx 大的组件宽,如果它们的首选尺寸决定的话。

希望这个简单的程序能够展示这个概念。尝试使用鼠标(或键盘)将 window 的大小调整得更宽,并观察每个文本字段如何增长:

import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;

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

public class GridBagProportions {
    static void buildAndShowWindow() {
        JTextField small = new JTextField("small (0.8)", 5);
        JTextField large = new JTextField("LARGE (0.2)", 30);

        small.setMinimumSize(small.getPreferredSize());
        large.setMinimumSize(large.getPreferredSize());

        JPanel panel = new JPanel(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets.left = 6;
        gbc.insets.top = 6;
        gbc.insets.bottom = 6;

        gbc.weightx = 0.8;
        panel.add(small, gbc);

        gbc.weightx = 0.2;
        gbc.insets.right = 6;
        panel.add(large, gbc);

        JFrame frame = new JFrame("GridBagLayout Proportions");
        frame.getContentPane().add(panel);
        frame.pack();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                buildAndShowWindow();
            }
        });
    }
}

那么可以做些什么呢?嗯,这是 GridBagLayout 做不到的一种布局场景。我会尝试使用 SpringLayout:

SpringLayout layout = new SpringLayout();
JPanel textAndUsers = new JPanel(layout);

SpringLayout.Constraints scrollPaneConstraints =
    new SpringLayout.Constraints(scrollPane);

Spring scrollPaneWidth = scrollPaneConstraints.getWidth();
SpringLayout.Constraints listConstraints =
    new SpringLayout.Constraints(scrollPaneWidth,
                                 scrollPaneConstraints.getY(),
                                 Spring.scale(scrollPaneWidth, 0.25f),
                                 scrollPaneConstraints.getHeight());

layout.putConstraint(SpringLayout.EAST, textAndUsers, 0,
                     SpringLayout.EAST, frmUserList);
layout.putConstraint(SpringLayout.SOUTH, textAndUsers, 0,
                     SpringLayout.SOUTH, scrollPane);

textAndUsers.add(scrollPane, scrollPaneConstraints);
textAndUsers.add(frmUserList, listConstraints);

请注意,listConstraints 的创建指定了宽度参数,即 Spring.scale(scrollPaneWidth, 0.25f)。这确保列表始终是包含 JTextArea 的 scrollPane 的四分之一宽。

SpringLayout 使用起来很棘手,因为您必须确保 link 布局容器的远边缘明确地指向子组件,因为 SpringLayout 不会自动增长以容纳所有子组件。这就是 putConstraint 调用正在做的事情。