4.8 布局设计
我们先看一下图 4.15。
图 4.15 常见的五种布局管理器设计方案
在该图中,按钮的个数及功能相同,但在界面显示上有很大的差异,这就是布局管理器 在起作用,布局管理器具有如下功能特点:
(1)布局管理器决定容器中组件的尺寸和位置。
(2)每个容器都有一个缺省的布局管理器。
(3)组件可以提供大小和排列的建议,但最终大小和位置由布局管理器决定。
(4)所有的 Panel(面板)对象在缺省状态下都使用 FlowLayout(流式布局)。
(5)内容窗格缺省使用 BorderLayout(边界布局),特点是菜单栏、工具栏的高度固定,
并且可以在界面上随意拖动。
AWT 的标准布局管理器有以下五种:
(1)FlowLayout:该布局管理器将组件从左到右或从上到下放置,是 Panel 和 Applet 的 默认布局管理器。
(2)BorderLayout:该布局管理器将组件分为东、西、南、北、中五个区域,是 Window、
Dialog 和 Frame 容器的默认布局管理器。
(3)CardLayout:该布局管理器将加入到容器的组件作为卡片,把每个组件放置在一个 单独的卡片上,后面的卡片将覆盖前面的卡片,每次只能看到一张卡片。
(4)GridLayout:该布局管理器将容器分为相同尺寸的网格,将组件依次放入网格中。
(5)GridBagLayout:该布局管理器将容器分为相同尺寸的网格,但组件不只是占一个网 格,一个组件可以占多个网格位置。
4.8.1 FlowLayout布局管理器
FlowLayout 型布局管理器对容器中组件进行布局的方式是将组件逐个地安放在容器中的 一行上,一行放满后就另起一个新行。
FlowLayout 有三种构造方法:
(1)public FlowLayout()
(2)public FlowLayout(int align)
(3)public FlowLayout(int align, int hgap, int vgap)
在默认情况下,FlowLayout 将组件居中放置在容器的某一行上,FlowLayout 的构造方法 中提供了一个对齐方式的可选项 align,align 可取值有三个:FlowLayout.LEFT(左对齐)、
FlowLayout.RIGHT(右对齐)、FlowLayout.CENTER(居中)。
【例 4.19】将三个按钮 button1、button2 和 button3 按 FlowLayout 布局方案放入框架 f 中。
//LayoutFlow.java import java.awt.*;
import java.awt.event.*;
class LayoutFlow { private Frame f;
private Button button1, button2, button3;
public static void main(String args[]) {
LayoutFlow layoutFlow = new LayoutFlow();
layoutFlow.go();
}
public void go() {
f = new Frame("Flow Layout");
f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {
System.exit(0);
} });
f.setLayout(new FlowLayout());
button1 = new Button("Ok");
button2 = new Button("Open");
button3 = new Button("Close");
f.add(button1);
f.add(button2);
f.add(button3);
f.setSize(100, 100);
f.setVisible(true);
} }
程序运行结果如图 4.16 所示。
4.8.2 BorderLayout布局管理器
BorderLayout 是 Dialog 类和 Frame 类的默认布局管理器,它提供了一种较为复杂的组件 布局管理方案,每个被 BorderLayout 管理的容器均被划分成五个区域:东(East)、南(South)、
西(West)、北(North)、中(Center)。North 在容器的上部,East 在容器的右部,其他依此 类推。Center 当然就是 East、South、West 和 North 所围绕的中部。
BorderLayout 布局管理器有两种构造方法:
(1)BorderLayout()构造一个各部分间距为 0 的 BorderLayout 实例。
(2)BorderLayout(int, int)构造一个各部分具有指定间距的 BorderLayout 实例。
在 BorderLayout 布局管理器的管理下,组件必须通过 add()方法加入到容器的五个命名区 域之一。
典型的边界型界面如图 4.17 所示。
aPanel.add(new Button("北区按钮"), BorderLayout.NORTH);
aPanel.add(new Button("南区按钮"), BorderLayout.SOUTH);
aPanel.add(new Button("西区按钮"), BorderLayout.WEST);
图 4.16 例 4.19 程序运行结果
aPanel.add(new Button("东区按钮"), BorderLayout.EAST);
边界型布局的每个区中只能放一个组件,如果在同一个区内放入第二个组件的话,后者 就会覆盖前者,可以使用内部容器在 BorderLayout 的一个区域内间接放入多个组件。
如果某个区域没有使用,那么它的大小将变为零,此时 Center 区域将会扩展并占据这个 未用区域的位置,如图 4.18 所示。
图 4.17 典型的边界型布局界面 图 4.18 缺少某个区域的边界型布局界面
实际上,常用的是上下排列,或者左右排列两个组件的情况。图 4.19 显示上下排列的边 界型布局和左右排列的边界型布局在调整高度和宽度时的表现。
图 4.19 上下排列的边界型布局和左右排列的边界型布局在调整高度和宽度时的布局界面
【例 4.20】将五个按钮 be、bw、bn、bs 和 bc 按 BorderLayout 布局方案放入框架 f 中。
///LayoutBorder.java import java.awt.*;
import java.awt.event.*;
class LayoutBorder { private Frame f;
private Button be, bw, bn, bs, bc;
public static void main(String args[]) {
LayoutBorder layoutBorder = new LayoutBorder();
layoutBorder.go();
}
void go() {
f = new Frame("Border Layout");
// 创建监听器对象,以关闭程序
f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {
System.exit(0);
} });
be = new Button("东区");
bs = new Button("南区");
bw = new Button("西区");
bn = new Button("北区");
bc = new Button("中间区域");
//设置布局管理器为 BorderLayout f.setLayout(new BorderLayout(20, 50));
f.add(be, BorderLayout.EAST);
f.add(bw, BorderLayout.WEST);
f.add(bs, BorderLayout.SOUTH);
f.add(bn, BorderLayout.NORTH);
f.add(bc, BorderLayout.CENTER);
TextField txtField = new TextField(20);
f.add(txtField);
f.setSize(350, 200);
f.setVisible(true);
GridLayout 有三种构造方法:
(1)public GridLayout()
(2)public GridLayout(int rows, int cols)
(3)public GridLayout(int rows, int cols, int hgap, int vgap)
第一种不带参数的构造方法创建一个只有一行的网格,网格的列数根据实际需要而定。
参数:rows 和 cols 分别指定网格的行数和列数,hgap 和 vgap 分别表示网格间的水平间距 和垂直间距。
如:new GridLayout(3,2),可以创建一个三行乘两列的布局管理器。
rows 和 cols 中的一个值可以为 0,但是两个值不能都是 0。
如果 rows 为 0,那么网格的行数将根据实际需要而定;如果 cols 为 0,那么网格的列数 将根据实际需要而定。
【例 4.21】将六个按钮 b1、b2、b3、b4、b5 和 b6 按 GridLayout 布局方案放入框架 f 中。
//LayoutDemo.java import java.awt.*;
import java.awt.event.*;
class LayoutGrid { private Frame f;
private Button b1,b2,b3,b4,b5,b6;
public static void main(String args[]) {
LayoutGrid layoutGrid = new LayoutGrid();
layoutGrid.show();
}
void show() {
f = new Frame("Layout example");
//f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// 创建监听器对象,以关闭程序
f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {
图 4.20 例 4.20 程序运行结果
System.exit(0);
} });
//将布局管理器设置为网格类型 f.setLayout(new GridLayout(0,2,10,20));
f.setSize(300,200);
b1 = new Button("b1");
b2 = new Button("b2");
b3 = new Button("b3");
b4 = new Button("b4");
b5 = new Button("b5");
b6 = new Button("b6");
f.add(b1);
f.add(b2);
f.add(b3);
f.add(b4);
f.add(b5);
f.add(b6);
TextField txtField = new TextField(30);
f.add(txtField);
f.pack();
f.setVisible(true);
} }
运行结果如图 4.21 所示。
图 4.21 例 4.21 程序运行结果
4.8.4 CardLayout布局管理器
CardLayout 是一种卡片式的布局管理器,卡片式布局是一种非常特殊的布局方式,它所 管理的不是组件,而是面板。采用卡片式布局的面板,会同时拥有若干个与它大小相同的子面 板,但在界面上每次只显示其中一个子面板。如同摞在一起的卡片一样,每次只能显示最顶上 的一张。
【例 4.22】卡片式布局管理器 CardLayout 应用示例。
//LayoutCard.java import java.awt.*;
import java.awt.event.*;
public class LayoutCard { CardLayout cardlay;
Panel panel;
Button b1;
int cardno = 0;
final int NCARDS = 4;
String labels[] = new String[NCARDS];
Frame frame;
public static void main(String[] args) {
LayoutCard layoutCard = new LayoutCard();
layoutCard.go();
}
void go() {
frame = new Frame("Border Layout");
//创建监听器对象,以关闭程序
frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {
System.exit(0);
} });
panel = new Panel();
cardlay = new CardLayout();
b1 = new Button("Next");
b1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt){
if (++cardno >= NCARDS) cardno = 0;
cardlay.show(panel, labels[cardno]);
} });
labels[0] = "Card One";
labels[1] = "Card Two";
labels[2] = "Card Three";
labels[3] = "Card Four";
panel.setLayout(cardlay);
for (int i = 0; i < NCARDS; i++)
panel.add(labels[i], new Label(labels[i]));
cardlay.show(panel, labels[0]);
frame.setLayout(new BorderLayout());
frame.add("Center",panel);
frame.add("South", b1);
frame.setSize(200,100);
frame.setVisible(true);
} }
程序运行结果如图 4.22 所示。
图 4.22 例 4.22 程序运行结果
4.8.5 GridBagLayout布局管理器
GridBagLayout 布局是最灵活、最复杂的布局之一,它比较接近表格型布局,但远比后者 灵活,可以定制每一格的大小、间隙等。GridBagLayout 的掌握有一定难度,下面首先通过一 个例子来直接演示这种布局的显示效果,再逐一分析这种效果是怎么做到的。
假设程序运行效果如图 4.23 所示。
图 4.23 例 4.23 程序运行结果
【例 4.23】表格包式布局管理器 GridBagLayout 应用示例。
//LayoutGridBag.java import java.awt.*;
import java.awt.event.*;
public class LayoutGridBag {
public static void main(String[] args) {
Frame frame = new Frame("表格包式布局");
// 创建监听器对象,以关闭程序
frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) {
System.exit(0);
} });
Panel panel = new Panel();
panel.setLayout(new GridBagLayout());
Button button;
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
button = new Button("新建文件");
c.weightx = 0.5;
c.gridx = 0;
c.gridy = 0;
panel.add(button, c);
button = new Button("打开");
c.gridx = 1;
c.gridy = 0;
panel.add(button, c);
button = new Button("另存为");
c.gridx = 2;
c.gridy = 0;
panel.add(button, c);
button = new Button("关闭(仅关闭打开的文件,而非整个程序)");
c.ipady = 40;
c.weightx = 0.0;
c.gridwidth = 3;
c.gridx = 0;
c.gridy = 1;
panel.add(button, c);
button = new Button("退出程序");
c.ipady = 0;
c.weighty = 1.0;
c.anchor = GridBagConstraints.PAGE_END;
c.insets = new Insets(10, 0, 0, 0);
c.gridx = 1;
c.gridwidth = 2;
c.gridy = 2;
panel.add(button, c);
frame.add(panel);
panel.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
panel.add(component, c);
代码的第一行设置布局管理器为表格包型,第二行代码产生一个约束对象,随后对约束 对象进行配置,最后调用 add 函数添加组件,add 函数的第二个参数为约束对象 c,组件的配 置信息就存储在约束对象 c 里,下面针对每个组件的约束对象 c 进行分析:
“新建文件”按钮有 4 个约束:
button = new Button("新建文件");
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 0;
c.weightx = 0.5;
panel.add(button, c);
“fill”:指示当组件的显示区域大于组件尺寸时,应当怎么办,缺省值是 NONE,也就是 说组件大小不变。其值有 HORIZONTAL(宽度),表示将组件拉宽到与单元格宽度一致,但 高度不变。VERTICAL(高度),表示将组件高度提升到与单元格高度一致,但宽度不变。BOTH
(同时),表示将组件的高度和宽度都拉伸到与单元格一致。本例中,这个约束是对所有组件 起作用的。
“gridx”,“gridy”:组件所在的单元格位置,例如:gridx = 0, gridy = 0,表示组件位于左 上角的单元格;gridx = 1, gridy = 2 表示组件位于第 2 列、第 3 行的单元格;建议对每个组件 都指定它所在的单元格。
“weightx”,“weighty”:确定当用户拉大主程序界面时,各单元格如何分配多余的空间,
这通过单元格的权重(weight)来确定,当用户拉大了窗口的宽度时,权重为 0 的单元格的宽 度不变,其他权重大于 0 的单元格按比例分配多出来的宽度。weighty 用于设置单元格高度的 权重,权重的绝对值不重要,关键是其相对比例。
第 4 个按钮引入了两个新的约束“gridwidth”和“ipady”,下面介绍这两个约束。
“gridwidth”,“gridheight”:分别表示这个组件在宽度和高度上分别占用了几个单元格。
变量的值表示单元格个数,而不是像素数。本例中,由于第 4 个按钮很长,一个单元格放不下,
因此设 gridwidth = 3,也就是横向占据 3 个单元格的位置,但纵向仍然只占据了一个单元格的 位置。
“ipadx”,“ipady”:设定组件内部的填充宽度或高度。由于填充是针对两边的,因此组件 的高度至少应为它的最小高度加上 ipady*2。本例中,为了提高第 4 个按钮的高度,同时将字 体居中,设置它的内部填充高度为 40。
第 5 个按钮又引入了两个新的约束“insets”和“anchor”,下面分别介绍。
“insets”:定义组件的外部填充,也就是说,在组件和单元格之间的空隙是多少。外部填 充值是通过一个 Insets 对象来指定的,它的构造函数的 4 个参数分别是组件顶部、左侧、底部、
右侧的填充值。
c.insets = new Insets(10,0,0,0);
本例中,指定第 5 个按钮的顶部与单元格之间有 10 个像素的空隙。
“anchor”:设定当组件小于单元格时,应该怎样放置单元格。组件有 9 种放置方式:
(1)FIRST_LINE_START(左上角)
(2)PAGE_START(上部的中间)
(3)FIRST_LINE_END(右上角)
(4)LINE_START(左侧的中间)
(5)CENTER(正中间)
(6)LINE_END(右侧的中间)
(7)LAST_LINE_START(左下角)
(8)PAGE_END(下部的中间)
(9)LAST_LINE_END(右下角)
9 种放置方式的位置图如图 4.24 所示。
图 4.24 组件的 9 种放置方式
在添加组件约束的时候,如果每个组件都共用一个约束,那么要注意还原设定值。例如 第 4 个按钮设定 ipady 为 40,但第 5 个按钮不需要设置内部填充,因此在定义第 5 个按钮的约 束时要将 ipady 设为 0,否则前面定义的约束会一直传给后面的组件。相反,本例中 fill 属性 就从头传到尾,为所有单元格所用。