# 如何使用 GroupLayout

> 原文： [https://docs.oracle.com/javase/tutorial/uiswing/layout/group.html](https://docs.oracle.com/javase/tutorial/uiswing/layout/group.html)

`GroupLayout`是为 GUI 构建器开发的布局管理器，例如 Matisse，随 NetBeans IDE 提供的 GUI 构建器。尽管布局管理器最初是为满足 GUI 构建器需求而设计的，但它也适用于手动编码。本讨论将教您`GroupLayout`的工作原理，并向您展示如何使用`GroupLayout`构建 GUI，无论您选择使用像 Matisse 这样的 GUI 构建器还是编写自己的代码。

* * *

**Note:** This lesson covers writing layout code by hand, which can be challenging. If you are not interested in learning all the details of layout management, you might prefer to use the `GroupLayout` layout manager combined with a builder tool to lay out your GUI. One such builder tool is the [NetBeans IDE](../learn/index.html). Otherwise, if you want to code by hand and do not want to use `GroupLayout`, then `GridBagLayout` is recommended as the next most flexible and powerful layout manager.

* * *

如果您对使用 JavaFX 创建 GUI 感兴趣，请参阅[在 JavaFX](https://docs.oracle.com/javase/8/javafx/layout-tutorial/index.html) 中使用布局。

## 设计原则：独立尺寸

`GroupLayout`分别与水平和垂直布局配合使用。为每个维度单独定义布局。在定义 _ 水平 _ 布局时，您不必担心 _ 垂直 _ 尺寸，反之亦然，因为沿每个轴的布局完全独立于沿另一个轴的布局。

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

## 布局组织：分层组

`GroupLayout`使用两种类型的排列 - 顺序和并行，结合分层组合。

1.  使用**顺序**排列，组件简单地一个接一个地放置，就像`BoxLayout`或`FlowLayout`沿着一个轴一样。每个组件的位置定义为相对于前一个组件。
2.  第二种方法将组件放在**并行**中 - 在同一空间中彼此顶部。它们可以沿垂直轴进行基线，顶部或底部对齐。沿水平轴，如果组件的大小不同，它们可以左对齐，右对齐或居中对齐。

通常，在一个维度上平行放置的组件在另一个维度中按顺序排列，因此它们不重叠。

这两种安排的强大之处在于它们可以分层嵌套。为此，`GroupLayout`定义**布局组**。组是顺序的或并行的，可以包含组件，其他组和间隙（下面讨论）。

顺序组的大小是所包含元素的大小的总和，并行组的大小对应于最大元素的大小（尽管，取决于元素和基线所在的位置，基线的大小 - 对齐组可能比最大元素稍大一些）。

定义布局意味着通过组合顺序和并行布置来定义组件应如何分组。

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

## 一个例子

让我们从简单的东西开始，连续只有三个组件：

![Three components in a row.](img/c0364203d2c614f1ed99fbff31523fd5.jpg)

我们将使用组来表达此布局。从水平轴开始，很容易看到从左到右排列有 3 个组件的 _ 顺序组 _。沿着垂直轴，存在相同 3 个组件的 _ 平行组 _，具有相同的位置，大小和基线：

![groups1a.](img/7a88ae2eebefccbb96a565088e01677b.jpg)

在伪代码中，布局规范可能如下所示（实际代码位于下面的 _ 编写代码 _ 部分）：

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

```

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

现在让我们再添加一个 C4 组件 C4 左对齐：

![example1b.](img/3a8111ee5d4c25aafde664b4a06f97dd.jpg)

沿水平轴，新组件占据与 C3 相同的水平空间，以便与 C3 形成平行组。沿垂直轴 C4 形成具有三个组件的原始平行组的顺序组。

![groups1b.](img/ed97726aed81daa2a4cb00710b37463e.jpg)

在伪代码中，布局规范现在看起来像这样：

```
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`区分（a）两个组分之间的优选间隙和（b）组分与容器边界之间的优选间隙。 `GroupLayout` API 中有相应的方法可以添加这些间隙（`addPreferredGap`和`addContainerGap`）。有三种类型的组分缺口：**相关**，**无关**和**缩进**。 `LayoutStyle.ComponentPlacement`枚举定义了相应的常量，用作`addPreferredGap`方法的参数：`RELATED`，`UNRELATED`和`INDENT`。相关和无关间隙之间的差异只是大小 - 不相关组件之间的距离稍微大一些。 _ 缩进 _ 表示两个组件的优选水平距离，当其中一个组件位于第二个组件下方并带有缩进时。

![gaps.](img/da454e742a56286d4a4ebf0973b33a40.jpg)

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

## 编写代码

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

让我们假设我们有一个名为`panel`的容器和已经呈现的相同的四个组件（`c1`，`c2`，`c3`和`c4`）。首先，我们创建一个新的`GroupLayout`对象并将其与面板关联：

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

```

我们指定自动插入间隙：

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

```

然后，我们定义组并添加组件。我们使用`setHorizontalGroup`和`setVerticalGroup`方法为每个维度建立根组。通过`createSequentialGroup`和`createParallelGroup`方法创建组。使用`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`枚举中定义的以下常数之一：`LEADING`，`TRAILING`，`CENTER`和`BASELINE`。这些常数用于两个维度，并取决于组件方向是从左到右还是从右到左（从上到下或从下到上）。例如，如果水平（垂直）分量方向是从左到右（从上到下），则`LEADING`表示左（上），而`TRAILING`表示右（下）。 `CENTER`在两个维度上都意味着“居中”。如果未指定对齐方式，则将使用`LEADING`。 `BASELINE`对齐仅在垂直维度上有效。

* * *

**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)`方法中的参数。这是两个例子：

1.  To force a component to be resizable (allow shrinking and growing):

    ```
    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`还可以控制封闭的并行组本身是否应该调整大小。如果抑制组大小调整，则会阻止包含的元素在组的首选大小上增长。这样，您可以在两侧对齐块组件，或者将各个组件限制为具有相同的大小。

让我们尝试从我们的示例（水平维度中的`c3`和`c4`）获得两个组件的相同大小：

```
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`的大小已经对应于组的大小。

结果，`c3`和`c4`将具有相同的宽度。组件不会进一步调整大小，因为并行组本身不可调整大小（上面`createParallelGroup`方法的第二个参数是`false`）。

![stretched.](img/f146a1e610e5d651cb483c491cf2adae.jpg)

细心的读者的问题：为什么我们在这个例子中将并行组中的两个组件定义为可调整大小？只有`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`首先检查是否为 Component 指定了值，如果没有，则检查全局属性的设置。

* * *

**Some history:** 

Java Standard Edition 6 中的`GroupLayout`由三个不同的工作组成：获取组件基线的能力，获得组件之间的优选间隙（`LayoutStyle`）和`GroupLayout`的能力。这项工作最初是作为 [http://java.net/projects/swing-layout/](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`或 swing-layout 中的`GroupLayout`为目标的功能。 NetBeans 目标的版本取决于项目所针对的 Java 平台版本。针对 Java SE 6 的项目使用 Java SE 中的`GroupLayout`，否则使用 swing-layout 中的`GroupLayout`。

* * *