网格组布局管理器GridBagLayout(又称为网格包布局管理器),是GridLayout的加强版,它是一个灵活的布局管理器,它不要求组件的大小相同便可以将组件沿垂直、水平或它们的基线对齐。每个GridBagLayout对象维持一个动态的单元网格矩形,每个组件占用一个或多个这样的单元网格,这些单元网格被称为显示区域。
每个由GridBagLayout管理的组件都需要由GridBagConstraints对象设置约束条件;Constraints 对象指定组件的显示区域在网格矩形中的具体放置位置,以及组件在其显示区域中的放置方式。除此之外还需考虑每个组件的最小大小和首选大小,以确定组件最终显示区域的大小。
使用网格组布局管理器的步骤:
创建一个GridBagLayout对象,并设置窗体容器的布局管理器为该对象。
创建组件。
给每个组件创建一个GridBagConstraints对象。设置好GridBagConstraints对象的约束属性,以确定该组件在网格组中的布局方案。
然后通过“add( component, constraints );”方法调用把组件放入容器中。
下面的例程,为了程序简明,我们把GridBagConstraints又封装为Gbc类。下面这段代码实现上面的几个步骤:
GridBagLayout layout = new GridBagLayout();
setLayout(layout); //给窗体设置布局管理器
//创建按钮A
JButton btnA = new JButton(“A”); //创建组件
Gbc gbcA = new Gbc(0, 0); //创建GridBagConstraints对象
gbcA.weightx=10; //设置另外的约束属性
gbcA.fill = Gbc.HORIZONTAL; //设置另外的约束属性
add(btnA, gbcA); //把组件放入容器
网格组布局管理器的难点是,如何正确设置GridBagConstraints对象的约束条件,另外理论上的预期结果有时与实际显示并不一致的,因此还是要经实际调试,以实际显示为准。下面我们对一些重要的约束属性进行简单介绍:
(1)属性 int gridx, gridy 默认值都为0
这两个属性gridx、gridy分别表示组件在网格矩形的行、列位置(X轴方向为gridx 、Y轴方向为gridy)。网格的坐标从0开始,其中X 向右递增,Y 向下递增。gridx=0和gridy=0表示网格坐标(0,0) 位于窗体容器的左上角(即第1行第1列);gridx=3和gridy=0表示第1行第4列。
(2)属性int gridwidth、gridheight 默认值都为 1
gridwidth、gridheight 分别指定组件的显示区域所占的网格单元数(gridwidth行、gridheight列)。gridwidth和gridheight默认值都为 1。这二个约束条件也可使用常量GridBagConstraints.REMAINDER(等同于整型数0)来指定组件的显示区域(表示组件行或列的显示区域一直延伸到行尾或列尾)。但是很遗憾的是与GridLayout布局中不同,单元网格大小不是固定大小的,实际显示区域可能与预期并不一致。
上面两个测试效果图出自同一个例程GridBagLayoutFrm.java,只是组件名长度不同。第一行的三个组件,水平方向的gridwidth属性值都是1、2、1。第一个图控件B的宽度约是A和C的2倍与预期结果相一致;但第二个图控件“网格A”、“网格B”、“网格C”的宽度好像都一样,“网格B”gridwidth属性值是2,好像与预期结果不一致;实际上组件“网格B”的二个单元网格的宽度好像缩小了一半。这就是令人疑惑不解的地方:组件的最小大小和首选大小不知是如何影响组件最终显示区域大小的。
(3)属性int fill 默认值为NONE(不填充)
fill,其字面意思是填充,也就是填充方式。只当控件显示区域大于单个单元网格时,fill属性才有效。默认值为NONE(不填充)。可用的取值还有BOTH(纵横双向填充)、HORIZONTAL(水平方向填充)、VERTICAL(垂直方向填充)。
(4)属性int anchor 默认值为CENTER(居中)
anchor,其字面意思是锚,也就是指控件显示时的停靠固定方式。anchor有很多种取值。绝对位置有8种取值分别是:NORTHWEST、NORTH、NORTHEAST、WEST、CENTER、EAST、SOUTHWEST、SOUTH、SOUTHEAST共9个方位。相对位置另有9种取值:FIRST_LINE_START、LINE_START、FIRST_LINE_END、PAGE_START、CENTER、PAGE_END、LAST_LINE_START、LINE_END、LAST_LINE_END。anchor还有其他取值。
如果组件没有填充满它的显示区域,此时可通过设置anchor来指定停靠方式。
(5)属性int ipadx、ipady 默认值为0
ipadx和ipady也被称为内部填充,该参数用以设置组件的最小尺寸,如果参数值为正值则组件的最小尺寸将比原始最小尺寸大,如果为负值,则组件的最小尺寸将会变得比原始的最小尺寸小。该参数也可以理解为直接为组件设定大小,这个设置是以组件的最小尺寸为基础。其设置后组件的大小为组件的原始最小尺寸加上ipadx*2个像素。ipadx用于修正组件的宽度;ipady则用于修正组件的高度。
(6)属性insets 默认值为 new Insets(0, 0, 0, 0)
insets被称为外部填充,指定组件边框周围的外部填充,即组件与其组件之间间距的最小量。要注意的是即使使用了fill参数填充横向和纵向,但只要设置了insets参数,同样会留出insets所设置的空白区域。
insets参数通过设置Insets对象的top、left、bottom和right四个方向的值来调整组件与组件之间间距大小,比如“gbc.insets = new Insets(10,10,10,10);”其中gbc是GridBagConstraints类型的约束对象,这里要注意后面的new Insets其中的Insets第一个字母是大写的。当然也可以为Insets指定负值参数,以扩大组件的显示区域。
(7)属性值 double weightx、weighty 默认值为0.0
weightx和weighty这两个属性参数可分别对x方向和y方向指定一个权重值。这两个参数与fill属性有关联性,当窗体缩放时有作用。当组件以最小尺寸显示时这个参数好像没有作用;若两参数使用默认值,则当窗体缩放时组件不改变大小;如果同一行中的组件都使用默认值,则窗体缩放时组件不改变大小,而且组件只显示在窗体中央。
这两参数可分别对x方向和y方向指定一个权重值。这个权重值直接影响到窗体放大时组件显示区域的大小,例如,同一行有三个组件,它们的fill属性为HORIZONTAL,当它们的weightx值分别为10,20,30,则在容器窗体的x方向三个组件按设定的权重分配每个组件的宽度,其中权重值越大的组件显示宽度越大,但好像也不是精确按指定比例显示大小。
如果同一行中的多个组件,只有一个组件设置了weightx值(非默认值),它们的fill属性为HORIZONTAL,当窗体缩放时,只有设置了weightx值的组件会动态放大,其他组件只则维持首选的组件尺寸大小不变。
属性参数weightx、weighty的优先级大于属性gridwidth、gridheight。例如:同一行放置二个组件,设置二个组件权重相同weightx=10,第一个组件gridwidth=2(占2个单元网格列),第二个组件gridwidth=1(占1个单元网格列),当窗体放大时,二个组件显示区域的宽度却是相同的。可自行编程测试。
因此在使用GridBagLayout网格包布局管理器时,一定要进行反复测试,程序的实际结果与你想象的预期结果不完全一样。
GridBagLayou是一种有弹性的布局管理器,而且网格单元可以不填满整个容器。下面的例程中我们演示了7个不同形状和大小按钮的摆放。这是一个很有意思的例程,你可注释掉一部分按钮或亦可修改一些属性参数,反复进行测试验证,有时候好像得不到预期的效果,有时候又显示预期效果,请测试体验并思考其中的原因,以加强对这种管理器约束条件的理解。
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.*;
public class GridBagLayoutTest extends JFrame {
public GridBagLayoutTest() {
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
//创建按钮A
JButton btnA = new JButton("A");
Gbc gbcA = new Gbc(0, 0);
gbcA.weightx=10;
gbcA.fill = Gbc.HORIZONTAL;
add(btnA, gbcA);
//创建按钮B
JButton btnB = new JButton("B");
Gbc gbcB = new Gbc(1, 0);
gbcB.weightx=20;
gbcB.fill = Gbc.HORIZONTAL;
gbcB.setInsets(0, 5, 0, 0);
add(btnB, gbcB);
//创建按钮C
JButton btnC = new JButton("C");
Gbc gbcC = new Gbc(2, 0, 1, 2);
gbcC.weightx=30;
gbcC.fill = Gbc.BOTH;
gbcC.setInsets(0, 5, 0, 0);
add(btnC, gbcC);
//创建按钮D
JButton btnD = new JButton("D");
Gbc gbcD = new Gbc(0, 1, 2, 1);
gbcD.fill = Gbc.HORIZONTAL;
gbcD.setInsets(5, 0, 0, 0);
add(btnD, gbcD);
//创建按钮E
JButton btnE = new JButton("E");
Gbc gbcE = new Gbc(3, 0, 1, 3);//new Gbc(3, 0, 1, Gbc.REMAINDER);
gbcE.weightx=40;
gbcE.fill = Gbc.VERTICAL;
gbcE.setInsets(0, 5, 0, 0);
add(btnE, gbcE);
//创建按钮F
JButton btnF = new JButton("F");
Gbc gbcF = new Gbc(0, 2, 1, 1);
gbcF.fill = Gbc.HORIZONTAL;
gbcF.setInsets(5, 0, 0, 0);
add(btnF, gbcF);
//创建按钮G
JButton btnG = new JButton("G");
Gbc gbcG = new Gbc(1,2, 2,1);
gbcG.fill = Gbc.HORIZONTAL;
gbcG.setInsets(5, 5, 0, 0);
add(btnG, gbcG);
setTitle("网格组布局测试");
pack();
setVisible(true);
}
public static void main(String[] args)
{ new GridBagLayoutTest(); }
}
class Gbc extends GridBagConstraints {
/**此构造器gridwidth和gridheight使用默认值1**/
public Gbc(int gridx,int gridy) {
this.gridx = gridx;
this.gridy = gridy;
}
public Gbc(int gridx,int gridy,int gridwidth,int gridheight) {
this.gridx = gridx;
this.gridy = gridy;
this.gridwidth = gridwidth;
this.gridheight = gridheight;
}
//设置外边距
public Gbc setInsets(int top,int left,int bottom,int right) {
this.insets = new Insets(top, left, bottom, right);
return this;
}
} //网格组布局管理器例程GridBagLayoutTest.java结束。
下面这二个测试效果图都是这个例程的:大图是窗口放大时的效果;小图是窗口未缩放时的效果。
大图中,组件A、组件B和组件C的权重比例是1:2:3。当窗体扩大时,组件B和C显示区域有所扩大,但好像也与设定的权重比例不完全一致。
下面是第二个测试例程GridBagLayoutFrm.java,请多做一些测试调试,以体验网络组布局管理器的特性。请仔细观测三种情形有何异同:
情形一:直接编译调试,对程序窗体进行缩放。
情形二:修改第一行的三个按钮名用单字母命名,再编译调试,对程序窗体进行缩放。
情形三:放开三个按钮gbc.weightx 的注释,再编译调试,对程序窗体进行缩放。
import java.awt.*;
import javax.swing.*;
public class GridBagLayoutFrm extends JFrame {
public GridBagLayoutFrm() {
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
//创建按钮A
JButton btn = new JButton("网格A");
Gbc gbc = new Gbc(0,0);//组件尺寸,默认1*1网格
//gbc.weightx = 10.0;
gbc.fill = Gbc.HORIZONTAL; //填充方式:水平填充
add(btn, gbc);
//创建按钮B
btn = new JButton("网格B");
gbc = new Gbc(1,0,2,1);//组件尺寸,2*1网格
//gbc.weightx = 20.0;
//gbc.fill = Gbc.BOTH;
gbc.fill = Gbc.HORIZONTAL;
add(btn, gbc);
//创建按钮C
btn = new JButton("网格C");
gbc = new Gbc(3, 0);
//gbc.weightx = 30.0;
gbc.fill = Gbc.HORIZONTAL;
add(btn, gbc);
//创建按钮D
btn = new JButton("网格D");
gbc = new Gbc(0,1,3,1);//组件尺寸,3*1网格
gbc.fill = Gbc.HORIZONTAL;
gbc.ipady = 20; //组件高度增加内部填充
add(btn, gbc);
//创建按钮E
btn = new JButton("网格E");
gbc = new Gbc(1, 2, 2, 1);
gbc.ipady=0;
gbc.weighty=1.0;
gbc.fill = Gbc.HORIZONTAL;
gbc.setInsets(10, 0, 0, 0); //增加外部填充
gbc.anchor = Gbc.PAGE_END; //停靠于页结束处
add(btn, gbc);
setTitle("网格组布局Demo");
pack();
setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(()->{
new GridBagLayoutFrm();
});
}
} //网格组布局管理器例程GridBagLayoutFrm.java结束。