文档

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

如何使用GridBagLayout


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

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

这是一个使用GridBagLayout的示例图片。

GridBagLayoutDemo的快照

点击“启动”按钮使用Java™ Web Start来运行GridBagLayoutDemo(下载JDK 7或更高版本)。或者,如果要编译和运行示例,请参考示例索引

启动GridBagLayoutDemo示例

GridBagDemo的代码位于GridBagLayoutDemo.java中。

GridBagLayout是Java平台提供的最灵活和复杂的布局管理器之一。它将组件放置在一个由行和列组成的网格中,允许指定的组件跨越多个行或列。并非所有的行高度都相同,同样的,并非所有的列宽度都相同。基本上,GridBagLayout将组件放置在一个网格的矩形(单元格)中,然后使用组件的首选大小来确定单元格的大小。

下图显示了上述小程序的网格。可以看到,网格有三行三列。第二行的按钮跨越了所有的列;第三行的按钮跨越了右边的两列。

GridBagLayoutDemo的快照,显示了它的网格

如果您放大窗口,如下图所示,您将注意到底部行(包含Button 5)获得了所有新的垂直空间。新的水平空间均匀分配给所有列。这种调整大小行为是基于程序对GridBagLayout中的各个组件分配的权重。您还会注意到,每个组件占用所有可用的水平空间 - 但不占用(如您在按钮5中所看到的那样)所有可用的垂直空间。这种行为也由程序指定。

用户放大后显示的GridBagLayout。

程序指定其组件的大小和位置特性的方式是为每个组件指定约束。设置组件约束的首选方法是使用Container.add变体,传递一个GridBagConstraints对象,如下面的示例所示。

下面的章节解释了您可以设置的约束,并提供了示例。

指定约束

以下代码是使用GridBagLayout的容器中典型的代码。在下一节中,您将看到一个更详细的示例。

JPanel pane = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();

//对于要添加到此容器的每个组件:
//...创建组件...
//...在GridBagConstraints实例中设置实例变量...
pane.add(theComponent, c);

您可能已经从上面的示例中猜到,即使组件具有不同的约束,也可以重用相同的GridBagConstraints实例。然而,建议您不要重用GridBagConstraints,因为如果您忘记为每个新实例重置字段,这很容易引入微妙的错误。


注意: 以下讨论假设GridBagLayout控制具有从左到右的组件方向的容器。

您可以设置以下GridBagConstraints实例变量:

gridx, gridy
指定组件左上角的行和列。最左边的列的地址为gridx=0,最上面的行的地址为gridy=0。使用GridBagConstraints.RELATIVE(默认值)指定该组件放置在刚在该组件之前添加到容器中的组件的右侧(对于gridx)或下方(对于gridy)。我们建议为每个组件指定gridxgridy值,而不仅仅使用GridBagConstraints.RELATIVE;这样可以得到更可预测的布局。
gridwidth, gridheight
指定组件显示区域的列数(对于gridwidth)或行数(对于gridheight)。这些约束条件指定组件使用的单元格数量,而不是像素数量。默认值为1。使用GridBagConstraints.REMAINDER指定该组件为其所在行(对于gridwidth)或列(对于gridheight)中的最后一个组件。使用GridBagConstraints.RELATIVE指定该组件为其所在行(对于gridwidth)或列(对于gridheight)中的倒数第二个组件。我们建议为每个组件指定gridwidthgridheight值,而不仅仅使用GridBagConstraints.RELATIVEGridBagConstraints.REMAINDER;这样可以得到更可预测的布局。

注意:GridBagLayout不允许组件跨越多行,除非组件位于最左边的列,或者您为组件指定了正数gridxgridy值。

fill
当组件的显示区域大于组件的请求大小时,用于确定是否以及如何调整组件的大小。有效的值(定义为GridBagConstraints常量)包括NONE(默认值),HORIZONTAL(使组件足够宽以水平填充其显示区域,但不更改其高度),VERTICAL(使组件足够高以垂直填充其显示区域,但不更改其宽度),BOTH(使组件完全填充其显示区域)。
ipadx, ipady
指定内部填充:要添加到组件大小的量。默认值为零。组件的宽度至少为其最小宽度加上ipadx*2像素,因为填充适用于组件的两侧。类似地,组件的高度至少为其最小高度加上ipady*2像素。
insets
指定组件的外部填充--组件与其显示区域边缘之间的最小空间量。该值以Insets对象的形式指定。默认情况下,每个组件都没有外部填充。
anchor
当组件比其显示区域小时,用于确定(在区域内)将组件放置在何处。有效值(定义为GridBagConstraints常量)为CENTER(默认值)、PAGE_STARTPAGE_ENDLINE_STARTLINE_ENDFIRST_LINE_STARTFIRST_LINE_ENDLAST_LINE_ENDLAST_LINE_START

下面是在具有默认从左到右的组件方向的容器中解释这些值的图片。

FIRST_LINE_START PAGE_START FIRST_LINE_END
LINE_START CENTER LINE_END
LAST_LINE_START PAGE_END LAST_LINE_END

版本注意事项: PAGE_**LINE_*常量在1.4版本中引入。之前的版本需要按照罗盘方位命名的值。例如,NORTHEAST表示显示区域的右上部分。我们建议您使用新的常量,因为它们更易于本地化。
weightx, weighty
指定权重是一个可以对GridBagLayout控制的组件外观产生重要影响的技巧。权重用于确定如何在列(weightx)和行(weighty)之间分配空间;这对于指定调整大小的行为非常重要。

除非您至少为weightxweighty指定一个非零值,否则所有组件都会集中在容器的中心。这是因为当权重为0.0(默认值)时,GridBagLayout将任何额外的空间放置在其单元格网格和容器边缘之间。

通常情况下,权重以0.0和1.0作为极端值:在中间的数字根据需要使用。较大的数字表示组件的行或列应该获得更多的空间。对于每一列,权重与该列内指定的组件的最高weightx相关联,每个多列组件的权重在其所在的列之间进行分割。类似地,每一行的权重与该行内指定的组件的最高weighty相关联。额外的空间倾向于向右下角的列和底部行。

下一部分在解释示例程序的工作原理的背景下详细讨论了约束。

示例解释

这里再次展示了GridBagLayoutDemo应用程序的图片。

GridBagLayoutDemo的快照

点击“启动”按钮使用Java™ Web Start运行GridBagLayoutDemo(下载JDK 7或更高版本)。或者,要自己编译和运行示例,请参考示例索引

启动GridBagLayoutDemo示例

下面的代码创建了GridBagLayout和它管理的组件。你可以在GridBagLayoutDemo.java中找到完整的源文件。

JButton button;
pane.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
if (shouldFill) {
                //natural height, maximum width
                c.fill = GridBagConstraints.HORIZONTAL;
}

button = new JButton("按钮1");
if (shouldWeightX) {
                   c.weightx = 0.5;
}
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
pane.add(button, c);

button = new JButton("按钮2");
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 1;
c.gridy = 0;
pane.add(button, c);

button = new JButton("按钮3");
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 0.5;
c.gridx = 2;
c.gridy = 0;
pane.add(button, c);

button = new JButton("长名称按钮4");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 40;      //使该组件变高
c.weightx = 0.0;
c.gridwidth = 3;
c.gridx = 0;
c.gridy = 1;
pane.add(button, c);

button = new JButton("5");
c.fill = GridBagConstraints.HORIZONTAL;
c.ipady = 0;       //重置为默认值
c.weighty = 1.0;   //请求额外的垂直空间
c.anchor = GridBagConstraints.PAGE_END; //底部对齐
c.insets = new Insets(10,0,0,0);  //顶部填充
c.gridx = 1;       //与按钮2对齐
c.gridwidth = 2;   //跨2列
c.gridy = 2;       //第三行
pane.add(button, c);

该示例使用一个GridBagConstraints实例来管理GridBagLayout的所有组件,然而在实际情况中,建议不要重用GridBagConstraints,因为如果你忘记为每个新实例重置字段,很容易引入细微的错误。在将每个组件添加到容器之前,代码会设置(或重置为默认值)GridBagConstraints对象中的适当实例变量。然后,它将组件添加到其容器,将GridBagConstraints对象作为add方法的第二个参数指定。

例如,要使按钮4变得特别高,示例代码如下:

c.ipady = 40;

在设置下一个组件的约束之前,代码将ipady的值重置为默认值:

c.ipady = 0;

如果组件的显示区域大于组件本身,则可以使用GridBagConstraints.anchor约束指定组件在显示区域中的显示位置。 anchor约束的值可以是绝对值(北,南,东,西等),或者是相对于方向的值(在页面开始,行末尾,第一行开始等),或者相对于组件基线的值。有关anchor约束的所有可能值的完整列表,包括相对于基线的值,请参阅GridBagConstraints.anchor的API文档。从上面的代码片段中可以看出,按钮5指定应该在显示区域的末尾显示,通过将锚点设置为GridBagConstraints.PAGE_END


注意:教程的示例以前使用了一种不同的方式来指定约束对象,您在其他程序中也可能会看到。我们的示例曾经使用GridBagLayout对象上的setConstraints方法来指定约束。例如:
GridBagLayout gridbag = new GridBagLayout();
pane.setLayout(gridbag);
...
gridbag.setConstraints(button, c);
pane.add(button);
但是,我们建议您使用Container.add方法,因为它比使用setConstraints更加简洁。

下表显示了GridBagLayoutDemo内容窗格中每个组件的所有约束条件。不是默认值的值用粗体标记。与上一个表项不同的值用斜体标记。

组件 约束条件
所有组件
ipadx = 0
fill = GridBagConstraints.HORIZONTAL
按钮1
ipady = 0 weightx = 0.5 weighty = 0.0 gridwidth = 1 anchor = GridBagConstraints.CENTER insets = new Insets(0,0,0,0) gridx = 0 gridy = 0
按钮2
weightx = 0.5
gridx = 1
gridy = 0
按钮3
weightx = 0.5
gridx = 2
gridy = 0
按钮4
ipady = 40
weightx = 0.0
gridwidth = 3
gridx = 0
gridy = 1
按钮5
ipady = 0
weightx = 0.0
weighty = 1.0
anchor = GridBagConstraints.PAGE_END
insets = new Insets(10,0,0,0)
gridwidth = 2
gridx = 1
gridy = 2

GridBagLayoutDemo有两个跨越多个列的组件(按钮4和按钮5)。为了使按钮4高,我们给它添加了内部填充(ipady)。为了在按钮4和按钮5之间添加空间,我们使用插入值在按钮5上方添加了最小10个像素,并且让按钮5贴紧其单元格的底部边缘。

pane容器中的所有组件都尽可能宽,给定它们所占用的单元格。程序通过将GridBagConstraintsfill实例变量设置为GridBagConstraints.HORIZONTAL来实现这一点,并在所有组件上保持该设置。如果程序没有指定填充,则按钮的宽度将是其自然宽度,如下所示:

默认填充值的GridBagLayoutDemo。

当你放大GridBagLayoutDemo的窗口时,列会按比例增长。这是因为第一行中的每个组件(每个组件为一列宽度)的weightx = 0.5。这些组件的weightx的实际值并不重要,重要的是所有的组件,因此所有的列都具有相等且大于0的权重。如果没有由GridBagLayout管理的组件设置了weightx,那么当容器的宽度增加时,组件将保持在容器的中心位置,如下所示:

默认weightx值的GridBagLayoutDemo,并由用户放大。

如果容器的大小小于或大于首选大小,那么任何空间都会根据GridBagContainer的权重进行分配。

请注意,如果您放大窗口,只有最后一行会变得更高。这是因为只有按钮5的weighty大于零。

GridBagLayout API

GridBagLayoutGridBagConstraints类只有一个构造函数,没有参数。您可以通过操作其实例变量来操纵GridBagConstraints对象,如指定约束中所述。通常,在GridBagLayout对象上只调用setConstraints方法,如解释示例中所示。

使用GridBagLayout的示例

在本教程中,您可以找到使用GridBagLayout的示例。以下表格列出了一些示例。

示例 描述位置 备注
GridBagLayoutDemo 本节 使用了许多特性 - 权重、内边距、内部填充、水平填充、精确单元格定位、多列单元格和锚定(组件在单元格内的定位)。
TextSamplerDemo 使用文本组件 对齐两组标签和文本字段,并在容器的整个宽度上添加一个标签。
ContainerEventDemo 如何编写容器监听器 使用权重、填充和相对定位在容器内部放置五个组件。

上一页: 如何使用FlowLayout
下一页: 如何使用GridLayout