Java教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
请参阅Java语言变化了解Java SE 9及其后续版本中更新的语言特性的摘要。
请参阅JDK发布说明了解有关所有JDK版本的新功能、增强功能以及已删除或不推荐选项的信息。
GroupLayout
是一种布局管理器,最初是为GUI构建工具(如NetBeans IDE提供的GUI构建工具Matisse)开发的。尽管该布局管理器最初是为GUI构建工具的需求而设计的,但它也适用于手动编码。本讨论将教你如何使用GroupLayout
以及如何使用GroupLayout
构建GUI,无论你选择使用像Matisse这样的GUI构建工具还是编写自己的代码。
GroupLayout
布局管理器结合构建工具来布局你的GUI。一个这样的构建工具是NetBeans IDE。否则,如果你想手动编码而又不想使用GroupLayout
,那么建议你使用GridBagLayout
作为下一个最灵活和强大的布局管理器。
如果你有兴趣使用JavaFX创建GUI,请参阅JavaFX中的布局。
GroupLayout
在水平和垂直布局中分别工作。布局在每个维度上是独立定义的。在定义水平布局时,你不需要担心垂直维度,反之亦然,因为每个轴上的布局完全独立于另一个轴上的布局。
当只关注一个维度时,你只需要一次解决一半的问题。这比同时处理两个维度要容易。当然,这意味着每个组件在布局中需要定义两次。如果你忘记这样做,GroupLayout
会生成一个异常。
GroupLayout
使用两种类型的排列——顺序和并行,结合层次组合。
BoxLayout
或FlowLayout
在一个轴上所做的那样。每个组件的位置是相对于前一个组件来定义的。通常,一个维度上并行放置的组件在另一个维度上是顺序的,以避免重叠。
这两种排列之所以强大,是因为它们可以以层次结构的方式嵌套。为此,GroupLayout
定义了布局组。一个组可以是顺序或并行的,可以包含组件、其他组和间隙(下面会讨论)。
一个连续组的大小是包含元素大小的总和,而并行组的大小对应于最大元素的大小(尽管根据元素和基线位置的不同,基线对齐组的大小可能会比最大元素稍大一些)。
定义布局意味着通过组合连续和并行排列来定义组件应该如何分组。
让我们用一个简单的示例来看看它在实践中是如何工作的。
我们从一些简单的东西开始,只有三个在一行中的组件:
我们将使用组来表示这个布局。从水平轴开始,很容易看出有一个从左到右的连续组包含了3个组件。沿着垂直轴有一个相同的并行组包含了相同的3个组件,位置、大小和基线都相同:
伪代码中,布局规范可能如下所示(真正的代码在下面的编写代码部分):
horizontal layout = sequential group { c1, c2, c3 } vertical layout = parallel group (BASELINE) { c1, c2, c3 }
这说明了前面提到的一个原则:在一个维度上按顺序分组的组件通常在另一个维度上形成一个并行组。
现在让我们再添加一个组件,C4,与C3左对齐:
沿着水平轴,新的组件占据与C3相同的水平空间,因此它与C3形成了一个并行组。沿着垂直轴,C4与原始的三个组件的并行组形成一个连续组。
现在,布局规范看起来像这样:
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中有相应的方法用于添加这些间隙(addPreferredGap
和addContainerGap
)。有三种类型的组件间隙:相关,无关和缩进。LayoutStyle.ComponentPlacement
枚举定义了用作addPreferredGap
方法参数的相应常量:RELATED
,UNRELATED
和INDENT
。相关间隙和无关间隙之间的区别只在于大小 - 无关组件之间的距离稍大一些。缩进表示当一个组件位于第二个组件下方时,两个组件之间的首选水平距离。
如上所述,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
对齐方式仅在垂直维度上有效。
组的布局中的对齐方式仅对大小不同的组件有效。相同大小的组件将自动对齐到每个 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)
方法的参数使用。以下是两个示例:
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
。
可调整大小的间隙
指定大小和可调整性也适用于间隙,包括首选间隙。例如,您可以指定两个组件之间的首选间隙,它起到将组件推开的作用(朝容器的相反两侧)。两个组件之间的首选距离仅用作间隙的最小大小。请参考以下代码片段:
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);
底层机制的工作原理如下:
c4
的大小已经对应于组的大小。
结果是,c3
和c4
的宽度将相同。组件不会再进一步调整大小,因为并行组本身不可调整大小(上面createParallelGroup
方法的第二个参数为false
)。
细心的读者可能会问:为什么在这个示例中我们将并行组中的两个组件都定义为可调整大小的?看起来只有
答案是:因为要实现平台和本地化的独立性。否则,我们将不得不依赖于
前面的情况是特殊的,因为组件在同一个并行组中。但是如果我们想要不相关的组件具有相同的大小呢?显然,通过分组不能始终确保相同的大小。对话框底部一行的确定和取消按钮就是一个很好的例子。为此,GroupLayout
提供了一个linkSize
方法。该方法允许将任意组件的大小链接在一起,而不管它们放置在何处。链接组件的结果大小根据最大的组件进行设置。例如:
layout.linkSize(SwingConstants.HORIZONTAL, c3, c4);
在这个示例中,大小在水平方向上被选择性地链接。
有两个重要的方法可以用于在运行时对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
。