到目前为止,已经整理了Java三大基本布局,即流布局、网格布局、边框布局。但对于复杂 的问题而言,只使用前面的这四种布局显然不够。在Windows中常常会发现很多应用程序没有解决好布局这个问题,比如“Cancel”在德语中为 “Abbrechen”,如果在开始拖动组件的时候,组件的大侠刚好设计成“Cancel”的大小,那么在德语版本中就会出现问题了,字符串将会被剪切掉 一部分。Windows中的按钮不能动态的增大以适应标签,因为用户界面设计的时候没有给出组件在那个方向增大的命令。组件只保存了像素位置和尺寸大小。 Java布局管理是一种用于组件布局的好方法,应用布局管理器,布局就可以使用组件间关系的指令来完成布局操作。
GridLayout(网格组布局)是Java中目前最灵活的布局管理器,但也由于其复杂性和某些行为的不直观,该布局管理器并不很受欢迎,不过按特定布 局排列组件时,只有GridBagLayout才具有足够的灵活性,因此该布局管理器虽然复杂但是任然常用。
GridBagLayout顾名思义,与GridLayout有些相似,不过只是表面上相似,除了都能将容器划分为网格之外,并无其他相同点,两者主要差别如下:
1.使用GridLayout时,组件的网格位置取决于组件添加到容器中的顺序,而使用GridBagLayout能够显式定义组件的网格位置。
2.GridLayout中的每一个组件恰好占用一个网格单元,而GridBagLayout管理的组件可以跨越多行或者多列网格。
3.GridLayout为没一行分配相同的高度,为每一列分配相同的宽度,使得网格单元具有相同的尺寸。而GridBagLayout敲好相反,允许每一行有各自的高度,每一列有各自的宽度,理论上使每个单元格具有不同的尺寸。
4.GridLayout不支持约束,而GridBagLayout允许每个组件指定不同的约束值,并用这些约束值定义组件在网格中的尺寸和位置。
GridBagLayout难点主要体现在:
1.约束数量与约束作用相互作用:GridBagConstraints中封装了11个约束值,每个子组件都会分配相应的GridBagConstraints实例,虽然单个约束并不复杂,但约束值之间的相互作用和约束与其他组件约束的相互作用就相当复杂了。
2.行高和列宽:为每一行提供单独高度、为每一列提供单独宽度是GridBagLayout的一大优点,但这也为使用GridBagLayout带来了难 度,某些情况下,尤其是在包含大量组件的复杂布局中,很难计算某个组件的尺寸和位置,因而容易犯错,导致布局结果异于设计要求。
3.组件位置:查看Grid中的组件时,不需要源代码也能看出组件所占用的网格单元。这是因为所有单元的尺寸相同且单元之间一一对齐,而要在GridLayout中找到组件所占用的单元则比较困难,因为当组件跨越多行或多列时,单元宽度和高度都会变化。
4.组件尺寸:其他大部分布局管理器都用建议规则来确定组件的实际尺寸,但GridBagLayout在这方面提供更强大的灵活性也伴随着更大的复杂性。
GridBagLayout只有一个无参的构造函数,因此创建实例很简单。GridBagLayout管理的容器中添加的每一个组件都关联一组约束值,这些约束值封装在GridBagConstraints类的实例中。
GridBagConstraints提供两个构造函数:无参构造函数和传递11个约束值的构造函数,尽管这两个构造函数都可使用,但后一个构造函数传递 的参数实在是太多了,即使熟悉GridBagLayout的人也难以理解使用,因此应尽量避免使用这种形式。此外Java中很少有这种情况,不实用 get/set方法来取值和赋值也能直接访问对象中字段的情况,实际上是因为GridBagLayout并未提供为其属性取值/赋值的方法,因此设置属性 就必须直接对属性赋值。
GridBagConstraints gbc=new GridBagConstraints();
gbc.gridx=0;
gbc.gridy=3;
GridBagLayout管理的容器添加组件使用add()方法并传递Component参数和约束的Object参数,当然也可以使用只有 Component引用参数的简化形式。不过使用该简化形式时,必须先调用GridBagLayout中的setConstraints()方法将组件和 约束值关联。
GridBagLayout gbLayout=new GridBagLayout();
setLayout(gbLayout);
GridBagConstraints gbc=new GridBagConstraints():
JButton button=new JButton("Test");
//需要先将按钮与一组约束相关联,然后都在添加到容器内
gbLayout.setConstraints(button,gbc);
add(button);
/**
*//除此之外,可以使用add()方法的另一种形式,传递表示约束信息的参数<br>
*add(button,gbc);
*
*/
上面的两种方法均可行,但对大多数人来说,使用后者更为直观,需要的代码更简洁。虽然容器添加的组件通常不止一个,且每个组件都有格子的约束值,但可以为所有组件使用同一个GridBagConstraints实例,因 为GridBagLayout管理的容器添加组件时,该布局管理器会使用GridBagConstraints的clone()方法进行行为的约束的深拷 贝。也就是说,添加组件时候,会复制组件关联的GridBagConstraints对象并将对象副本保存到GridBagLayout中以备引用 。因此可以重复使用同一个GridBagConstraints对象,由该布局管理器负责复制该对象。总结上面的使用GridBagLayout就是:
1.建立一个GridBagLayout的对象。不需要指定网格的行数和列数。布局管理器会根据后面所给的信息猜测出来
2.将GridBagLayout设为该容器的默认布局管理器
3.为每一个组件建立一个GridBagConstraints对象(可使用同一个GridBagConstraints对象,需针对每个子组件设置其属性),设置GridBagConstraints对象的域以便指出组件在网格组中的布局方案
4.最后,(推荐)通过下面的调用添加组件的约束:add(component,constraints);
GridBagConstraints对象的状态对GridBagLayout来说是非常重要的,下面详细介绍几个最重要的约束。
1:gridx、gridy,gridwidth、gridheight
这些约束定义了组件在网格中的位置。gridx和gridy指定了被添加组件左上角的行、列位置,gridwidth和gridheight指定了组件占 据的行数和列数。网格的坐标从(0,0)开始,gridx=0和gridy=0代码最左上角。gridx=2,gridy=0,gridwidth=1、 gridheight=4表示该约束起始于0行2列,横跨了4行1列。
gridx和gridy默认值都是GridBagConstraints.RELATIVE,gridx使用默认值==
指定将组件放置在添加此组件之前刚刚添加到容器中的组件的后面,gridy使用默认值==
指 定将组件放置在添加此组件之前刚刚添加到容器中的组件的下面。此外,gridx和gridy都是非负值,awt文档中建议不要将gridx和gridy设 置为绝对位置,推荐使用使用相当于位置GridBagConstraints.RELATIVE,然后按照标准的顺序,将组件添加到网格组布局中 。
gridheight和gridwidth非负,默认为1。一般可以指定一个适当的值来设置组件跨越的行数或者列数,但是如果希望组件扩展到最好一行或者 最后一列,则不要给出一个实际的数值,而是用常量GridBagConstraints.REMAINDER替代,这样会告诉布局管理器这个组件是本行上 的最后一个组件。
2:增量域
在网格布局中,需要为每个区域设置增量域(weightx和weighty)。如果增量域设置为0.0(默认值),则在这个区域将永远为初始尺寸,增加的多余空间会被左右平均、上下平均分配到组件的四周,即让组件显示在分配区域的中间。
从概念上讲,增量参数属于行和列的属性,而不是属于某个单元格。但却需要在单元格中指定它们,因为网格组布局并不会暴露行和列。行和列的增量等于每行或每 列单元格的增量最大值。因此,想让某行或者某列保持不变,就需要将这行或这列的所有组件的增加都设置为0。
3:fill和anchor
默认情况下,组件尺寸应为其首选尺寸或最小尺寸,而不是分配给组件的单元尺寸。如果需要组件在区域内拉伸可设置其fill约束。当组件的显示区域大于组件区域的大小时使用此字段。它可以确定是否调整组件大小,以及在需要的时候如何进行调整。
-
NONE
:使用组件自然尺寸0。(默认) -
HORIZONTAL
:加宽组件,使它在水平方向上填满其显示区域,但是不改变高度。 -
VERTICAL
:加高组件,使它在垂直方向上填满其显示区域,但是不改变宽度。 -
BOTH
:使组件完全填满其显示区域。
当组件小于分配区域时( 即组件没有填充至整个区域 )。可以通过设置anchor 域指定其位置。 可能的值有三种:
相对于方向的值 : 相对于方向的值是相对于容器的组件方向属性进行解释的,
PAGE_START:沿显示区域的边缘居中放置组件,等同于水平方向的 NORTH
PAGE_END:沿显示区域的边缘居中放置组件,等同于水平方向的 SOUTH。
LINE_START:沿显示区域的边缘居中放置组件,在该区域中,等同于水平的、从左到右方向的 WEST,以及水平的、从右到左方向的 EAST。
LINE_END:沿显示区域的边缘居中放置组件,在该区域中,等同于水平的、从左到右方向的 EAST,以及水平的、从右到左方向的 WEST。
FIRST_LINE_START:将组件置于其显示区域的一角。等同于水平的、从左到右方向的 NORTHWEST,以及水平的、从右到左方向的 NORTHEAST。
FIRST_LINE_END:将组件置于显示区域的一角,在该区域中。等同于水平的、从左到右方向的 NORTHEAST,以及水平的、从右到左方向的 NORTHWEST。
LAST_LINE_START:将组件置于其显示区域的一角。等同于水平的、从左到右的 SOUTHWEST,以及水平的、从右到左方向的 SOUTHEAST。
LAST_LINE_END:将组件置于其显示区域的一角。等同于水平的、从左到右的 SOUTHEAST,以及水平的、从右到左方向的 SOUTHWEST。
相对于基线的值 : 相对于基线值是相对于基线进行解释,
BASELINE : 指定组件应该沿主要行的基线水平居中和垂直对齐。如果组件没有基线,那么它会垂直居中对齐。
BASELINE_LEADING : 指定组件应沿开始边水平放置。对于从左到右方向的组件,开始边是左边。组件沿主要行的基线垂直对齐。如果组件没有基线,那么它会垂直居中对齐。
BASELINE_TRAILING : 指定组件应沿结尾边水平放置。对于从左到右方向的组件,结尾边是右边。组件沿主要行的基线垂直对齐。如果组件没有基线,那么它会垂直居中对齐。
ABOVE_BASELINE : 指定组件应该水平居中对齐。垂直放置组件,使它的底边接触到开始行的基线。如果开始行没有基线,那么它会垂直居中对齐。
ABOVE_BASELINE_LEADING : 指定组件应沿开始边水平放置。对于从左到右方向的组件,开始边是左边。垂直放置组件,使它的底边接触到开始行的基线。如果开始行没有基线,那么它会垂直居中对齐。
ABOVE_BASELINE_TRAILING : 指定组件应沿结尾边水平放置。对于从左到右方向的组件,结尾边是右边。垂直放置组件,使它的底边接触到开始行的基线。如果开始行没有基线,那么它会垂直居中对齐。
BELOW_BASELINE : 指定组件应该水平居中对齐。垂直放置组件,使它的底边接触到开始行的基线。如果开始行没有基线,那么它会垂直居中对齐。
BELOW_BASELINE_LEADING : 指定组件应沿开始边水平放置。对于从左到右方向的组件,开始边是左边。垂直放置组件,使它的底边接触到开始行的基线。如果开始行没有基线,那么它会垂直居中对齐。
BELOW_BASELINE_TRAILING : 指定组件应沿结尾边水平放置。对于从左到右方向的组件,结尾边是右边。垂直放置组件,使它的底边接触到开始行的基线。如果开始行没有基线,那么它会垂直居中对齐。
绝对值 :
CENTER(默认值):将组件置于其显示区域的中心。
NORTH:将组件置于其显示区域的顶部,并在水平方向上居中。
NORTHEAST:将组件置于其显示区域的右上角。
EAST:将组件置于其显示区域的右部,并在垂直方向上居中。
SOUTHEAST:将组件置于其显示区域的右下角。
SOUTH:将组件置于其显示区域的底部,并在水平方向上居中。
SOUTHWEST:将组件置于其显示区域的左下角。
WEST:将组件置于其显示区域的左部,并在垂直方向上居中。
NORTHWEST:将组件置于其显示区域的左上角。
4.填塞(Insets属性和ipadx,ipady)
通过指定GridBagConstraints的insets域在组件周围增加附加的空白区域,通过设置Insets对象的left、top、right 和bottom指定组件周围的空间量,这种被称之为外部填塞。等同于其他布局管理器中所用的水平间隙和垂直间隙,不过GridBagLayout的这一约 束更加灵活可以为每个组件设置不同的Insets对象,可以在组件的每一边设置不同的间隙值。要设置组件的插入值,可以创建Insets类的对象实例,或 者创建GridBagConstraints对象时修改自动创建的Insets实例。最后需要注意的是:组件不允许覆盖组件显示区域的插入部分,就算用了 fill约束拉伸组件也不行。
通过设置ipadx和ipady称之为内部填塞,这两个值会被添加到组件的最小宽度和最小高度上面,这样可以保证组件不会收缩至最小尺寸以下。这两个属性默认为0。
参考:
1.《 Pro Java Programming(Second Edition) 》
2.《 Core Java I 》