我正在观察使用GroupLayout的一些奇怪行为。我有一个JTextArea,它包含在JScrollPane内,该控件正在调整大小并将其他组件推出JFrame。奇怪的是,如果我重新安排布局,以使JTextArea在其上方或下方都没有任何内容(也没有任何间隙),则可以正常工作。就像文本区域正在询问容器一样,容器中有多少空间,然后占用其100%的空间,而与其他组件无关。另一个奇怪的事情是,它似乎仅在JTextArea(不是JScrollPane)的大小加上容器内其他组件的高度达到Short.MAX_VALUE时才会发生。

如果我在垂直 Pane 中为滚动 Pane 指定了最大大小(将组件添加到布局时),将其最大大小设置为小于Short.MAX_VALUE,那么似乎可以解决该问题(只要该值与Short之间存在差异即可)。 MAX_VALUE大于所有其他组件的高度)。例如
.addComponent(textArea, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE - 500)
另外,如果我将首选大小设置为较小的正值,而不是GroupLayout.PREFERRED_SIZE或GroupLayout.DEFAULT_SIZE,它似乎也会使这种行为消失。例如
.addComponent(textArea, 0, 1, Short.MAX_VALUE)
GroupLayout上的Java教程似乎没有提及任何内容,而是倾向于在各处使用Short.MAX_VALUE。我尝试使用Google搜索来找到答案,但发现该问题很难用搜索字词来描述。

我是否已找到错误,还是只是不了解GroupLayout?当然后者似乎更有可能。

本示例将创建一个简单的文本区域。按下下部按钮以在其中填充文本(并在JScrollPane中调整JTextArea的大小)。然后,您可以在文本区域内单击并添加或删除行。添加一些额外的行后,单击“重绘”按钮(或调整框架的大小)以查看奇怪的行为。

public class GroupLayoutTest {
    public GroupLayoutTest() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                final JFrame frame = new JFrame("GroupLayout test");
                Container panel = frame.getContentPane();

                GroupLayout layout = new GroupLayout(panel);
                panel.setLayout(layout);

                JButton addBtn = new JButton("Add Lines");
                JButton redrawBtn = new JButton("Redraw");

                final JTextArea textArea = new JTextArea();
                final JScrollPane textPane = new JScrollPane(textArea);

                layout.setHorizontalGroup(layout.createParallelGroup()
                        .addComponent(redrawBtn)
                        .addComponent(textPane)
                        .addComponent(addBtn));

                layout.setVerticalGroup(layout.createSequentialGroup()
                        .addComponent(redrawBtn)
                        .addComponent(textPane)
                        .addComponent(addBtn));

                addBtn.addActionListener(new ActionListener() {
                    int m = 0;

                    @Override
                    public void actionPerformed(ActionEvent e) {
                        for (int i = m; m < i + 2044; ++m) {
                            textArea.append("Line " + m + "\n");
                        }

                        // redraw the frame
                        frame.validate();
                    }
                });

                redrawBtn.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        frame.validate();
                    }
                });

                frame.setPreferredSize(new Dimension(640, 480));
                frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static void main(String[] args) {
        new GroupLayoutTest();
    }
}

最佳答案

我对GroupLayout不太熟悉,这只是我从查看文档并进行思考时所观察到的。希望它会有所帮助。我的回答有点长,所以我将总结如下:

为什么会这样?三件事的结合。

  • GroupLayout尊重组件的首选大小。 (这就是您根据评论来选择它的原因)。
  • 您的垂直组是连续的。它一次布置一个组件-以它们的首选大小,如果有空间,则更大。
  • 您的JScrollPane具有不受限制的首选大小。无论JTextArea的首选大小是多少,它都会增长……一旦它的首选大小变得大于框架的大小,就不会在其下面的组件上留下任何空间。

  • 通常,将尊重首选大小的布局管理器与首选大小非常大的组件组合在一起通常会导致此问题。我还没有进行广泛的测试,但是我知道它也会在FormLayout中发生(如果您告诉它使用首选的大小)。

    较长的解释/思考过程尝试:



    文本区域不是在询问容器可以达到多大的大小-相反,它只是在告诉容器要达到多大的大小。在这种情况下,textArea直接包含在滚动 Pane 中。文本区域的首选大小将随着文本区域中文本的大小而增长。滚动 Pane 的首选大小将随文本区域的首选大小而增长,除非您在滚动 Pane 上设置了首选大小。从那里,布局管理器将决定如何布局。

    在这种情况下,您要使用带有SequentialGroup的GroupLayout进行垂直布局。这将根据组件的最小/首选/最大大小来按顺序排列组件。如果将textPane重新定位为组中的最后一个项目,则不会从其他组件中窃取空间...因此,我的猜测是,对于非常大的尺寸,GroupLayout不在乎是否显示所有组件-如果在最后一个组件之后没有剩余空间,直到容器放大后它们才会显示。对于小尺寸,用户可以调整框架的大小...但是对于像您的示例中的大尺寸,这是不可行的。

    与遗漏区分开来-看起来只是一个巨大的滚动 Pane 和GroupLayout的组合不知道何时停止...因此,滚动 Pane 栏的底部被切除了。但是,正如@camickr所建议的,只要在JScrollPane上设置合理的首选大小,就可以避免所有这些情况。



    如果注销每个组件的大小,将会看到,除非为JScrollPane指定首选大小,否则它将始终具有比textArea更大的首选大小,因此我认为上述说法不正确。滚动 Pane 的大小以及容器中其他组件的大小仍将超过Short.MAX_VALUE,并且确实会导致GroupLayout中出现问题,这是事实。

    我看到的JScrollPane的要点是将一个具有较大(或可能较大)的首选大小的组件容纳在一个较小的区域中(适当时使用滚动条)。然后,您可以始终让滚动窗​​格增大到大于其首选大小的大小...但是首先要告诉滚动 Pane 应该有多大。实际上,设置首选大小实际上是在告诉JScrollPane需要滚动条时。例如,如果滚动 Pane 的首选大小是400,300,则只要所包含组件的首选宽度超过400(或者滚动 Pane 的大小,如果它在容器中并使它增长到超过其首选大小)即可。 ,显示水平滚动条。同样,对于高度也是如此。否则,它总是会增长到您所拥有的大小,并且不需要滚动条,消除该点,或者在某些情况下,不会显示其他组件。

    GroupLayout的文档提到它通常由其他布局构建器工具使用,而通常不被开发人员使用(尽管您仍然可以使用)。我会考虑在通常需要您使用特殊的最大值才能正常工作的布局上使用其他布局。我个人最喜欢的是FormLayout,它是免费的,BSD许可的第三方布局管理器。我认为您不必为常规组件指定固定的大小,但是在滚动 Pane 的情况下,您确实希望在尊重首选大小的布局中为其指定首选大小。

    我希望我所有的布局都遵守DPI,在适当的地方进行扩展,并能很好地与国际化配合使用(其中文本长度可能会大不相同)...因此,我不想使用任何需要为所有内容指定固定大小的东西。我认为在大多数布局中,在滚动 Pane 上设置固定的首选大小并不是一件坏事。在按钮,文本字段或其他显然具有明显首选大小的组件上执行此操作,而不是那么多。

    这是在FormLayout中的外观(它也具有生成器,但是在这种情况下,我使用单元格约束轻松地跨越一列):
    FormLayout layout = new FormLayout(
          "pref,fill:pref:grow", // cols
          "pref,3dlu,fill:pref:grow,3dlu,pref" // rows
    );
    
    JPanel panel = new JPanel(layout);
    
    CellConstraints cc = new CellConstraints();
    
    panel.add(redrawBtn, cc.xy(1, 1));
    panel.add(textPane, cc.xyw(1, 3, 2)); // span 2 columns
    panel.add(addBtn, cc.xy(1, 5));
    
    frame.setContentPane(panel);
    

    10-08 18:58