FlowLayout 的意外行为

Posted

技术标签:

【中文标题】FlowLayout 的意外行为【英文标题】:Unexpected behavior of FlowLayout 【发布时间】:2020-10-04 17:15:33 【问题描述】:

我正在使用 JPanel 和默认的 FlowLayout 布局管理器。我认为如果应用程序窗口被调整大小并且没有足够的宽度在一行中显示JPanel 中的所有组件,一些组件将被移动到另一行。

此假设基于文档:

如果容器中的水平空间太小,无法将所有组件放在一行中,FlowLayout 类会使用多行。

https://docs.oracle.com/javase/tutorial/uiswing/layout/flow.html

它在某些情况下有效。例如,在使用此代码并减小窗口宽度时,按钮将位于不同的行:

import javax.swing.*;
import java.awt.*;

public class FlowLayoutExpected 
    public static void main(String[] args) 
        JFrame frame = new JFrame();
        JPanel panel = new JPanel();

        JButton button1 = new JButton("Button 1");
        JButton button2 = new JButton("Button 2");
        JButton button3 = new JButton("Button 3");

        panel.add(button1);
        panel.add(button2);
        panel.add(button3);

        frame.getContentPane().add(BorderLayout.CENTER, panel);

        frame.setSize(350, 70);
        frame.setVisible(true);
    

在减小宽度之前 -> 减小宽度后

但是将一个JPanel 放入另一个JPanel 就足够了,布局管理器就会停止按预期运行:

import javax.swing.*;
import java.awt.*;

public class FlowLayoutReal 
    public static void main(String[] args) 
        JFrame frame = new JFrame();
        JPanel outerPanel = new JPanel();
        JPanel innerPanel = new JPanel();

        JButton button1 = new JButton("Button 1");
        JButton button2 = new JButton("Button 2");
        JButton button3 = new JButton("Button 3");

        innerPanel.add(button1);
        innerPanel.add(button2);
        innerPanel.add(button3);

        outerPanel.add(innerPanel);
        frame.getContentPane().add(BorderLayout.CENTER, outerPanel);

        frame.setSize(350, 70);
        frame.setVisible(true);
    

在减小宽度之前 -> 减小宽度后

我试图澄清我误解了什么。


我已经阅读了很多使用 WrapLayout 而不是 FlowLayout 的建议。这似乎是合理的,但我仍然不清楚为什么我在上面的示例中会出现不一致的行为。

【问题讨论】:

见***.com/questions/52234113/… ...重点是:你的期望是有缺陷的。你不能指望像那样“简单地”在面板内推动面板。所有这些布局管理器都有一定的“属性”和用例,“流内流”不一定是受支持的用例。换句话说:如果您需要更复杂的 UI,那么您可以通过使用更复杂的布局管理器来实现,而不是将简单的布局管理器组合在一起。 @GhostCat,是的,我明白你的意思。我将使用其他布局管理器来处理复杂的 UI。我提供了一个简化的示例,我不会无缘无故地将 JPanel 放在 JPanel 上 :-) 同时,我在文档或其他地方找不到任何我不能这样做的语句。所以我的意图是了解我在文档中遗漏的内容,以避免将来出现此类问题(以及其他文档)。 “我在文档或其他地方找不到任何声明,为什么我不能这样做。”代码是最终的文档。请注意,您不能将 FlowLayout 放在开发人员笔记本中 BorderLayout 内的 FlowLayout 内,然后继续处理下一个问题。 @GilbertLeBlanc,“代码是终极文档。” - 简单但有用的提醒。谢谢。 【参考方案1】:
frame.getContentPane().add(BorderLayout.CENTER, panel);

首先,该方法自 JDK1.1 以来已“过时”。

此后的首选方法是添加“约束”作为第二个参数:

frame.getContentPane().add(panel, BorderLayout.CENTER);

但是将一个 JPanel 放入另一个 JPanel 并且布局管理器停止按预期运行就足够了:

代码按预期工作。问题在于你的期望。

更改第一个示例中的代码:

//frame.getContentPane().add(panel, BorderLayout.CENTER);
frame.getContentPane().add(panel, BorderLayout.PAGE_START);

现在,当您减小宽度时,组件就会消失。这是因为BorderLayout.PAGE_START 将尊重所添加组件的首选高度。首选高度是通过在一行上显示所有组件来确定的。

但是将一个 JPanel 放入另一个 JPanel 并且布局管理器停止按预期运行就足够了

当您开始包装面板时,您需要了解其中的含义。

FlowLayout 尊重添加的所有组件的首选大小。所以内部面板中的组件都按预期显示在一行中。

当你将外面板添加到BorderLayout.CENTER时,外面板的大小会调整,但不会影响内面板,因为FlowLayout的规则,说添加的任何组件都显示在面板的顶部.由于内部面板是单个组件,因此无需包装。

这是一个经常用于确保组件保持其首选大小的技巧。

另一个例子。也许你有一个水平的按钮GridLayout 想要添加到BorderLayout.PAGE_START 的面板上。

如果直接添加面板,那么按钮的大小会随着框架的变化而缩小/增长。

如果您希望按钮大小保持不变,您可以使用“包装”面板。然后“包装”面板尺寸缩小/增长,但按钮尺寸保持不变。

您需要了解布局管理器的规则,才能有效地使用它。

【讨论】:

camickr,感谢您的精彩解释。这个问题现在对我来说很清楚了。老实说,起初,我不明白为什么在更改窗口大小时内部面板没有调整大小(就像外部面板一样),但后来,我检查了 FlowLayout 和 BorderLayout 中 layoutContainer() 方法的区别,现在我明白原因了。

以上是关于FlowLayout 的意外行为的主要内容,如果未能解决你的问题,请参考以下文章

Android 自定义View 手写瀑布流组件FlowLayout

自定义流式布局

WindowsLookAndFeel关于按钮着色的意外行为

Swift中的嵌套函数意外行为?

UIlabel 大小以适应意外行为

连接字符串的意外行为