文档

Java™教程
隐藏目录
如何使用GroupLayout
路径: 使用Swing创建GUI
课程: 在容器内布置组件

如何使用GroupLayout

GroupLayout是一种布局管理器,最初是为GUI构建工具(如NetBeans IDE提供的GUI构建工具Matisse)开发的。尽管该布局管理器最初是为GUI构建工具的需求而设计的,但它也适用于手动编码。本讨论将教你如何使用GroupLayout以及如何使用GroupLayout构建GUI,无论你选择使用像Matisse这样的GUI构建工具还是编写自己的代码。


注意: 本课程涵盖了手动编写布局代码,这可能具有一定的挑战性。如果你对布局管理的所有细节不感兴趣,你可能更喜欢使用GroupLayout布局管理器结合构建工具来布局你的GUI。一个这样的构建工具是NetBeans IDE。否则,如果你想手动编码而又不想使用GroupLayout,那么建议你使用GridBagLayout作为下一个最灵活和强大的布局管理器。

如果你有兴趣使用JavaFX创建GUI,请参阅JavaFX中的布局

设计原则:独立的维度

GroupLayout在水平和垂直布局中分别工作。布局在每个维度上是独立定义的。在定义水平布局时,你不需要担心垂直维度,反之亦然,因为每个轴上的布局完全独立于另一个轴上的布局。

当只关注一个维度时,你只需要一次解决一半的问题。这比同时处理两个维度要容易。当然,这意味着每个组件在布局中需要定义两次。如果你忘记这样做,GroupLayout会生成一个异常。

布局组织:分层组

GroupLayout使用两种类型的排列——顺序和并行,结合层次组合。

  1. 使用顺序排列,组件会依次放置,就像BoxLayoutFlowLayout在一个轴上所做的那样。每个组件的位置是相对于前一个组件来定义的。
  2. 第二种方式是并行排列——在同一空间中上下堆叠放置组件。在垂直轴上,它们可以基线对齐、顶部对齐或底部对齐。在水平轴上,如果组件的大小不同,它们可以左对齐、右对齐或居中对齐。

通常,一个维度上并行放置的组件在另一个维度上是顺序的,以避免重叠。

这两种排列之所以强大,是因为它们可以以层次结构的方式嵌套。为此,GroupLayout定义了布局组。一个组可以是顺序或并行的,可以包含组件、其他组和间隙(下面会讨论)。

一个连续组的大小是包含元素大小的总和,而并行组的大小对应于最大元素的大小(尽管根据元素和基线位置的不同,基线对齐组的大小可能会比最大元素稍大一些)。

定义布局意味着通过组合连续和并行排列来定义组件应该如何分组。

让我们用一个简单的示例来看看它在实践中是如何工作的。

一个示例

我们从一些简单的东西开始,只有三个在一行中的组件:

Three components in a row.

我们将使用组来表示这个布局。从水平轴开始,很容易看出有一个从左到右的连续组包含了3个组件。沿着垂直轴有一个相同的并行组包含了相同的3个组件,位置、大小和基线都相同:

groups1a.

伪代码中,布局规范可能如下所示(真正的代码在下面的编写代码部分):

horizontal layout = sequential group { c1, c2, c3 }
vertical layout = parallel group (BASELINE) { c1, c2, c3 }

这说明了前面提到的一个原则:在一个维度上按顺序分组的组件通常在另一个维度上形成一个并行组。

现在让我们再添加一个组件,C4,与C3左对齐:

example1b.

沿着水平轴,新的组件占据与C3相同的水平空间,因此它与C3形成了一个并行组。沿着垂直轴,C4与原始的三个组件的并行组形成一个连续组。

groups1b.

现在,布局规范看起来像这样:

horizontal layout = sequential group { c1, c2, parallel group (LEFT) { c3, c4 } }
vertical layout = sequential group { parallel group (BASELINE) { c1, c2, c3 }, c4 }

现在,你已经了解了使用GroupLayout设计布局的最重要的方面。还有一些更多的细节要解释:如何添加间隙,如何定义大小和调整大小的行为,如何定义对齐布局,以及如何编写真正的代码。

间隙

间隙可以被视为具有特定大小的不可见组件。间隙可以像组件或其他组一样添加到组中。使用间隙,您可以精确控制组件之间或与容器边界之间的距离。

GroupLayout还定义了自动间隙,这些间隙对应于相邻组件之间(或组件与容器边框之间)的首选距离。这种间隙的大小是根据应用程序使用的外观和感觉动态计算的(使用LayoutStyle类进行计算)。使用自动(首选)间隙有两个优点:您不必指定间隙的像素大小,它们会自动根据UI的外观和感觉进行调整,以反映实际的外观和感觉准则。

GroupLayout区分了两个组件之间的首选间隙和组件与容器边框之间的首选间隙。GroupLayout API中有相应的方法用于添加这些间隙(addPreferredGapaddContainerGap)。有三种类型的组件间隙:相关无关缩进LayoutStyle.ComponentPlacement枚举定义了用作addPreferredGap方法参数的相应常量:RELATEDUNRELATEDINDENT。相关间隙和无关间隙之间的区别只在于大小 - 无关组件之间的距离稍大一些。缩进表示当一个组件位于第二个组件下方时,两个组件之间的首选水平距离。

gaps.

如上所述,GroupLayout可以自动插入间隙 - 如果您没有显式添加自己的间隙,它会为您添加相关首选间隙。但是,这不是默认行为。您必须通过在GroupLayout上调用setAutoCreateGaps(true)setAutoCreateContainerGaps(true)来打开此功能。然后,您将自动获得正确的间隔。

编写代码

现在,让我们来看一下创建上述布局的实际代码。

假设我们有一个名为panel的容器和相同的四个组件(c1c2c3c4)。首先,我们创建一个新的GroupLayout对象并将其与面板关联:

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

我们指定自动插入间隙:

layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);

然后,我们定义组和添加组件。我们使用setHorizontalGroupsetVerticalGroup方法为每个维度建立根组。组通过createSequentialGroupcreateParallelGroup方法创建。使用addComponent方法将组件添加到组中。

layout.setHorizontalGroup(
   layout.createSequentialGroup()
      .addComponent(c1)
      .addComponent(c2)
      .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
           .addComponent(c3)
           .addComponent(c4))
);
layout.setVerticalGroup(
   layout.createSequentialGroup()
      .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
           .addComponent(c1)
           .addComponent(c2)
           .addComponent(c3))
      .addComponent(c4)
);

您可以为并行组指定对齐方式。可以使用 GroupLayout.Alignment 枚举中定义的以下常量之一: LEADINGTRAILINGCENTERBASELINE。这些常量用于两个维度,并取决于组件的方向是从左到右还是从右到左(从上到下还是从下到上)。例如,如果水平(垂直)组件方向是从左到右(从上到下),LEADING 表示左(上),而 TRAILING 表示右(下)。CENTER 表示两个维度上的"居中"。如果不指定对齐方式,将使用 LEADINGBASELINE 对齐方式仅在垂直维度上有效。


注意: 

组的布局中的对齐方式仅对大小不同的组件有效。相同大小的组件将自动对齐到每个 GroupLayout.Alignment 常量。


关于代码的一些注释:

组件大小和可调整性

布局中的可调整组件数量没有限制。

GroupLayout 中的每个组件的大小由三个值限制;最小大小、首选大小和最大大小。这些大小控制组件在布局中的调整大小方式。 GroupLayout.addComponent(...) 方法允许指定大小限制。

如果没有明确指定,布局会请求组件的默认大小(使用组件的 getMinimumSize()getPreferredSize()getMaximumSize() 方法)。对于大多数组件,您不需要指定任何内容,例如使 JTextField 可调整大小或使 JButton 固定大小,因为这些组件本身具有所需的调整大小行为。另一方面,您可以覆盖默认行为。例如,您可以将 JTextField 设置为固定大小,或者将 JButton 设置为可调整大小。

GroupLayout 定义了一些常量,可以精确控制调整大小的行为。它们可以作为 addComponent(Component comp, int min, int pref, int max) 方法的参数使用。以下是两个示例:

  1. 强制使组件可调整大小(允许缩小和扩大):
    group.addComponent(component, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ...
    

    这允许组件在最小尺寸(零大小)和任意尺寸(Short.MAX_VALUE 表示“无限大”)之间调整大小。如果我们不希望组件在其默认最小尺寸以下缩小,我们可以在第二个参数中使用 GroupLayout.DEFAULT_SIZE 替代 0

  2. 使组件大小固定(禁止调整大小):
    group.addComponent(component, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE,
              GroupLayout.PREFERRED_SIZE) ...
    

在这些示例中,组件的初始大小不会改变,其默认大小是组件的首选大小。如果我们希望组件具有特定的大小,我们可以在第二个参数中指定它,而不是使用 GroupLayout.DEFAULT_SIZE

可调整大小的间隙

指定大小和可调整性也适用于间隙,包括首选间隙。例如,您可以指定两个组件之间的首选间隙,它起到将组件推开的作用(朝容器的相反两侧)。两个组件之间的首选距离仅用作间隙的最小大小。请参考以下代码片段:

layout.createSequentialGroup()
    .addComponent(c1)
    .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED,
                     GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    .addComponent(c2);

并行组中的大小设置

放置在并行组中的可调整大小元素会被拉伸以填充由组中最大元素确定的空间,因此它们最终以相同的大小对齐。 GroupLayout 还可以控制封闭并行组本身是否应该调整大小。如果禁止组的调整大小,它将阻止包含的元素超过组的首选大小。通过这种方式,您可以使一组组件在两侧对齐,或者限制单个组件具有相同的大小。

让我们尝试为示例中的两个组件(c3c4 在水平方向上)实现相同的大小:

layout.createParallelGroup(GroupLayout.Alignment.LEADING, false)
  .addComponent(c3, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
  .addComponent(c4, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE);

底层机制的工作原理如下:

  1. 并行组的大小设置为最大元素的首选大小;在我们的示例中,为 c4的首选大小。
  2. 可调整大小的元素会被拉伸到组的大小。在我们的示例中,只有 c3被有效地拉伸, c4的大小已经对应于组的大小。

结果是,c3c4的宽度将相同。组件不会再进一步调整大小,因为并行组本身不可调整大小(上面createParallelGroup方法的第二个参数为false)。

stretched.

细心的读者可能会问:为什么在这个示例中我们将并行组中的两个组件都定义为可调整大小的?看起来只有 c3是可拉伸的,因为 c4无论如何都不会被拉伸...

答案是:因为要实现平台和本地化的独立性。否则,我们将不得不依赖于 c4组件始终比 c3大。但是当应用程序在不同平台上运行或者被翻译成另一种语言时,这可能会改变。通过使两个组件都可调整大小,它们可以相互调整大小,无论哪个在某一时刻更大。

使组件大小相同

前面的情况是特殊的,因为组件在同一个并行组中。但是如果我们想要不相关的组件具有相同的大小呢?显然,通过分组不能始终确保相同的大小。对话框底部一行的确定和取消按钮就是一个很好的例子。为此,GroupLayout提供了一个linkSize方法。该方法允许将任意组件的大小链接在一起,而不管它们放置在何处。链接组件的结果大小根据最大的组件进行设置。例如:

layout.linkSize(SwingConstants.HORIZONTAL, c3, c4);

在这个示例中,大小在水平方向上被选择性地链接。

运行时更改GUI

有两个重要的方法可以用于在运行时对GUI进行更改:replace()setHonorsVisibility()。使用这两个方法,您可以在运行时交换组件或更改组件的可见性,并使GUI相应地重新排列。

replace(Component existingComponent, Component newComponent)用新组件替换现有组件。对于动态布局,常见的操作之一就是能够像这样替换组件。例如,可能有一个复选框在显示图形或树的组件之间切换。GroupLayout通过replace()方法使这种情况变得简单。您可以在不重新创建所有组的情况下交换组件。

用户界面中的另一个常见操作是动态更改组件的可见性。可能只有在用户完成表单的早期部分时才显示组件。为了避免在这种情况下组件重新排列,应该占用空间,而不管组件的可见性如何。GroupLayout提供了两种配置不可见组件的方式。setHonorsVisibility(boolean)方法全局设置不可见组件的处理方式。默认值为true,表示不可见组件被视为不存在。另一方面,false的值为不可见组件提供空间,将其视为可见。setHonorsVisibility(Component,Boolean)方法可用于在特定组件级别上配置行为。为了确定如何处理可见性,GroupLayout首先检查是否为组件指定了值,如果没有,则检查全局属性的设置。


一些历史: 

Java标准版6中的GroupLayout由三个不同的工作组成:获取组件基线的能力,获取组件之间的首选间隙(LayoutStyle),以及GroupLayout。这项工作最初是作为一个开源项目在http://java.net/projects/swing-layout/上完成的。

NetBeans 5.0通过swing-layout项目支持GroupLayout。由于这项工作的成功,所有三个部分已经合并到Java标准版6中的GroupLayout中。Java SE 6中的GroupLayout与swing-layout的主要区别在于包名和方法名。NetBeans 5.5提供了针对Java SE 6中的GroupLayout或swing-layout中的GroupLayout的能力。NetBeans所针对的版本取决于项目所针对的Java平台版本。针对Java SE 6的项目使用Java SE中的GroupLayout,否则使用swing-layout中的GroupLayout



上一页:如何使用GridLayout
下一页:GroupLayout示例