如何使用 GroupLayout

GroupLayout是为 GUI 构建器(例如 Matisse)开发的布局 管理 器,Matisse 是 NetBeans IDE 随附的 GUI 构建器。尽管布局 管理 器最初是为满足 GUI 构建器的需求而设计的,但它对于手动编码也很有效。该讨论将教您GroupLayout的工作方式,并向您展示如何使用GroupLayout来构建 GUI,无论您选择使用 Matisse 之类的 GUI 构建器还是编写自己的代码。

Note:

本课涵盖了手工编写布局代码,这可能会很困难。如果您不希望了解布局 管理 的所有详细信息,则可能更喜欢将GroupLayout布局 管理 器与构建器工具结合使用来布局 GUI。 NetBeans IDE是此类构建器工具之一。否则,如果您想手工编码并且不想使用GroupLayout,那么推荐使用GridBagLayout作为下一个最灵活,功能最强大的布局 管理 器。

如果您对使用 JavaFX 创建 GUI 感兴趣,请参阅在 JavaFX 中使用布局

设计原则:独立尺寸

GroupLayout分别处理水平和垂直布局。分别为每个尺寸定义布局。定义水平布局时,您无需担心垂直尺寸,反之亦然,因为沿每个轴的布局完全独立于沿另一个轴的布局。

当只关注一个维度时,您只需一次解决一半的问题。这比一次处理两个尺寸要容易。当然,这意味着每个组件需要在布局中定义两次。如果您忘记执行此操作,GroupLayout将生成一个异常。

布局组织:层次组

GroupLayout使用两种类型的安排-Sequences 安排和并行安排,并结合了层次结构。

  • 通过“Sequences”排列,组件可以简单地一个接一个地放置,就像BoxLayoutFlowLayout沿一个轴进行放置一样。每个组件的位置都定义为相对于先前的组件。

  • 第二种方式是将组件“Parallel”放置在同一空间中彼此重叠。它们可以沿垂直轴基线对齐,顶部对齐或底部对齐。如果组件的尺寸不相同,则沿水平轴可以左对齐,右对齐或居中对齐。

通常,在一个维度上 Parallel 放置的组件在另一个维度上是按 Sequences 排列的,因此它们不会重叠。

使这两种安排功能强大的原因在于它们可以按层次嵌套。为此,GroupLayout定义 布局组 。一个组可以是连续的,也可以是并行的,并且可以包含组件,其他组和间隔(在下面讨论)。

Sequences 组的大小是所包含元素大小的总和,而 Parallel 组的大小则对应于最大元素的大小(尽管取决于元素和基线所处的位置,基线的大小对齐的组可能比最大元素大一点)。

定义布局意味着通过组合 Sequences 排列和并行排列来定义应如何对组件进行分组。

让我们用一个简单的例子来看看它是如何工作的。

An Example

让我们从简单的内容开始,仅连续三个部分:

连续三个部分。

我们将使用组来表达此布局。从水平轴开始,很容易看到从左到右依次排列的 3 个组件。沿着垂直轴,存在由相同的三个组件组成的 Parallel 组,它们具有相同的位置,大小和基线:

groups1a.

在伪代码中,布局规范可能看起来像这样(实际代码在下面的“编写代码”部分中):

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

这说明了前面提到的原理:在一维中按 Sequences 分组的组件通常在另一维中形成并行组。

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

example1b.

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

groups1b.

用伪代码,布局规范现在看起来像这样:

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

现在,您了解了使用GroupLayout设计布局的最重要方面。这里仅需解释一些细节:如何添加间隙,如何定义大小和调整大小行为,如何定义合理的布局以及如何编写实际代码。

Gaps

间隙可以认为是一定大小的不可见成分。可以将任意大小的间隙添加到组中,就像组件或其他组一样。使用间隙,您可以精确控制组件之间或容器边缘的距离。

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

GroupLayout区分(a)两个组件之间的首选间隙和(b)组件与容器边界之间的首选间隙。 GroupLayout API 中有相应的方法来添加这些间隙(addPreferredGapaddContainerGap)。组件差距有三种类型: 相关不相关缩进LayoutStyle.ComponentPlacement枚举定义了用作addPreferredGap方法的参数的相应常量:RELATEDUNRELATEDINDENT。相关间隙和不相关间隙之间的差异只是大小上不相关的组件之间的距离要大一些。缩进表示当两个组件中的一个通过凹口位于第二个组件下方时,两个组件的首选水平距离。

gaps.

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

Writing Code

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

让我们假设我们有一个名为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)
);

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

Note:

组布局中的对齐仅对不同大小的组件有意义。对于每个GroupLayout.Alignment常量,相同大小的组件将自动对齐。

有关代码的一些 注解:

  • 您无需将组件直接添加到容器中-使用 addComponent 方法之一隐式为您完成。

  • 请注意用于填充组的addComponent方法的链接调用。 addComponent方法始终返回调用它的组。因此,您不需要使用局部变量来保存组。

  • 缩进代码是个好主意,这样很容易看到组的层次结构。给每个组件换行,为层次结构中的每个新组添加一个缩进级别。一个好的源代码编辑器将帮助您配对括号以关闭createXXXGroup方法。通过遵循这些简单的规则,可以轻松添加新组件或删除现有组件。

组件大小和可缩放性

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

GroupLayout中每个组件的大小受三个值约束;最小尺寸,首选尺寸和最大尺寸。这些大小控制组件在布局中如何调整大小。 GroupLayout.addComponent(...)方法允许指定大小限制。

如果未明确指定,则布局会向组件询问其默认大小(通过使用组件的getMinimumSize()getPreferredSize()getMaximumSize()方法)。您不需要为大多数组件指定任何内容,例如使JTextField可调整大小或JButton固定,因为这些组件本身具有默认的所需调整大小行为。另一方面,您可以覆盖默认行为。例如,您可以使JTextField固定或JButton可调整大小。

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

  • 强制调整组件大小(允许缩小和增 Long):
group.addComponent(component, 0, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ...

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

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

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

Resizable gaps

指定大小和可缩放性也适用于间隙,包括首选间隙。例如,您可以指定两个组件之间的首选间隙,其作用就像是一个 Spring,将组件彼此推开(移向容器的相对侧)。这两个分量的首选距离仅用作间隙的最小大小。请参见以下代码段:

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

并行组中的大小

放置在 Parallel 组中的可调整大小的元素会拉伸以填充由组中最大元素确定的组空间,因此它们final以相同的大小对齐。 GroupLayout还控制封闭的并行组本身是否应调整大小。如果抑制了组大小调整,则会阻止包含的元素在组的首选大小上增 Long。这样,您可以使一个组件块在两侧对齐,或将单个组件约束为具有相同的大小。

让我们try使示例中的两个组件达到相同的大小(水平尺寸为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);

底层机制的工作方式如下:

  • 并行组的大小设置为最大元素的首选大小;因此在我们的示例中为c4的首选大小。

  • 可调整大小的元素被拉伸到组的大小。在我们的示例中,只有c3被有效拉伸,c4的大小已经对应于组的大小。

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

stretched.

细心 Reader 的问题:在本示例中,为什么我们将并行组中的两个组件都定义为可调整大小的?由于c4仍未拉伸,因此似乎只有c3可调整大小...

答案是:由于平台和本地化的独立性。否则,我们将不得不依靠c4总是大于c3的分量。但是,当应用程序在不同平台上运行或翻译为另一种语言时,这可能会改变。通过使两个组件可调整大小,它们可以相互调整,而不管给定 Moment 哪个更大。

使组件具有相同的大小

前一种情况是特殊的,因为组件在同一并行组中。但是,如果我们希望不相关的组件具有相同的大小怎么办?显然,不能总是通过分组来确保相同的大小。对话框底部连续的“确定”和“取消”按钮就是一个很好的例子。为此,GroupLayout提供了linkSize方法。此方法允许链接任意组件的大小,而不管它们放在何处。链接的组件的final大小是根据最大的组件设置的。例如:

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

在此示例中,尺寸针对水平尺寸进行选择性链接。

GUI 的运行时更改

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

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

用户interface中的另一种常见操作是动态更改组件的可见性。可能仅在用户完成表单的较早部分时才显示组件。为了避免组件在这种情况下混乱,无论组件的可见性如何,都应占用空间。 GroupLayout提供了两种方法来配置不可见组件的处理方式。 setHonorsVisibility(boolean)方法全局设置不可见组件的处理方式。默认值 true 表示不可见的组件被视为不存在。另一方面,值 false 为不可见的组件提供了空间,将它们视为可见。 setHonorsVisibility(Component,Boolean)方法可用于在特定组件级别配置行为。为了确定可见性的处理方式,GroupLayout首先检查是否已为 Component 指定了一个值,如果没有,则检查 global 属性的设置。

Some history:

Java Standard Edition 6 中的GroupLayout由三个不同的工作主体组成:获得组件基线的能力,获得组件之间的首选差距(LayoutStyle)和GroupLayout的能力。这项工作最初是在http://java.net/projects/swing-layout/作为一个开源项目完成的。

NetBeans 5.0 通过 swing-layout 项目支持GroupLayout。由于这项工作的成功,所有这三个部分在 Java Standard Edition 版本 6 中都被卷成GroupLayout。Java SE 6 中的GroupLayout和 swing-layout 之间的主要区别在于程序包名称和方法名称。 NetBeans 5.5 提供了针对 Java SE 6 中的GroupLayout或摆动布局中的GroupLayout的功能。 NetBeans 所针对的版本取决于您的项目所针对的 Java 平台的版本。针对 Java SE 6 的项目在 Java SE 中使用GroupLayout,否则在摆幅中使用GroupLayout