界面设计里,一个容器要放许多组件。为了美观,给组件安排在容器中的位置,就是布局设计。java.awt 定义了多种布局类,每种对应一种策略。
常用布局:FlowLayout 依次放置;BorderLayout 放在边界上;CardLayout 像扑克牌叠放,每次只显示一张;GridLayout 按行列划成相等格子;GridBagLayout 划成许多小矩形单元,一个组件可占一格或多格。GridBagLayout 能精细控制也最复杂,本教程暂不讨论。
每个容器都有布局管理器,决定如何安排组件。布局管理器是实现 LayoutManager 接口的类。
一、FlowLayout(JApplet、JPanel、JScrollPane 默认)
按加入顺序从左到右排,一行满了换下一行,每行组件居中。最简便,适合组件不多的情况;组件多时会高低不平、各行长短不一。
构造方法:
-
FlowLayout():默认居中,间隙 5 像素
-
FlowLayout(int alignment):对齐方式为 LEFT / CENTER / RIGHT(原文 aligment 为拼写笔误)
-
FlowLayout(int alignment, int horz, int vert):同时设定水平间距 horz 和垂直间距 vert
用超类 Container 的 setLayout() 设定布局,例如 setLayout(new FlowLayout())。加入组件用 add(组件名)。
二、BorderLayout(JWindow、JFrame、JDialog 默认)
把容器分成东 East、西 West、南 South、北 North、中 Center 五个区域。加入时要指明区域,一个位置一个组件。某位置要放多个组件时,先放进另一个容器,再把该容器加到这个位置。
构造:BorderLayout() 默认无间隙;BorderLayout(int horz, int vert) 设定水平、垂直间距。设定方法 setLayout(new BorderLayout())。加入用 add(组件名, 位置),不指定位置则默认为“中”。
【例 11-5】五个标签分别放在窗口东、西、南、北、中:
import javax.swing.*;
import java.awt.*;
public class J505 {
public static void main(String[] args) {
JLabel label1, label2, label3, label4, label5;
JFrame mw = new JFrame("我是一个窗口"); // 创建一个窗口容器对象
mw.setSize(250, 200);
Container con = mw.getContentPane();
con.setLayout(new BorderLayout());
label1 = new JLabel("东标签"); // 默认左对齐
label2 = new JLabel("南标签", JLabel.CENTER);
label3 = new JLabel("西标签");
label4 = new JLabel("北标签", JLabel.CENTER);
label5 = new JLabel("中标签", JLabel.CENTER);
con.add(label1, "East");
con.add(label2, "South");
con.add(label3, "West");
con.add(label4, "North");
con.add(label5, "Center");
mw.setVisible(true);
}
}
三、GridLayout
把容器划成若干行、列网格,行数和列数由程序控制,组件放在格子里,定位比较精确。每个网格形状大小相同,要求组件也保持相同大小。
-
-
GridLayout(int row, int col):指定行、列
-
GridLayout(int row, int col, int horz, int vert):再加水平、垂直间距
以行为基准:组件超额时自动增加列,太少则减少列,行数不变,按行优先排列。每个网格必须填组件,空白处可用空标签 add(new Label()) 顶替。
【例 11-6】若干按钮和标签放入 JPanel,再放入 JScrollPane,最后放入窗口。滚动面板可视范围小于面板实际大小,可拖滑块看原先不可见区域:
import java.applet.*;
import javax.swing.*;
import java.awt.*;
class MyWindow extends JFrame {
public MyWindow(int w, int h) {
setTitle("滚动面板实例");
Container con = getContentPane();
con.setPreferredSize(new Dimension(w, h));
con.setLayout(new BorderLayout());
JPanel p = new JPanel();
p.setLayout(new GridLayout(6, 6));
for (int i = 0; i < 6; i++) {
p.add(new JLabel());
for (int j = 1; j <= 2; j++) {
p.add(new JButton("按钮" + (2 * i + j)));
p.add(new JLabel("标签" + (2 * i + j)));
}
p.add(new JLabel());
}
p.setBackground(Color.blue);
p.setPreferredSize(new Dimension(w + 60, h + 60));
JScrollPane scrollPane = new JScrollPane(p);
scrollPane.setPreferredSize(new Dimension(w - 60, h - 60));
add(scrollPane, BorderLayout.CENTER); // 添加滚动面板
setVisible(true);
pack();
}
}
class ScrollPane extends JScrollPane {
public ScrollPane(Component p) {
super(p);
setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
}
}
public class J506 extends Applet {
MyWindow myWindow;
public void init() {
myWindow = new MyWindow(400, 350);
}
}
GridLayout 要求组件大小一致,界面可能不好看。补救:小组件先合并进一个容器,再把该容器当作组件放入 GridLayout,即容器嵌套。例如容器 A 用 GridLayout;B、C 各放若干组件后再加入 A。B、C 也可以是 GridLayout 或其他布局,外观上各组件大小就有了差异。
四、CardLayout
容器可容纳多个组件,但它们共用同一显示空间,某一时刻只显示一个,像一叠扑克每次只露最上面一张,该组件占满容器。步骤:先创建 CardLayout 对象,再用 setLayout() 设置,最后 add()。加入方式是 add(组件代号, 组件),代号是另给的字符串,与组件名无关。
CardLayout myCard = new CardLayout(); // 创建 CardLayout 布局对象
JPanel p = new JPanel(); // 创建 Panel 对象
p.setLayout(myCard);
显示某一组件有两种方式:
-
show(容器名, 组件代号),例如 myCard.show(p, k)
-
按加入顺序:
first / last / next / previous,例如 myCard.first(p)
【例 11-7】面板 p 用 CardLayout 放 10 个标签;窗口 4 个按钮分别显示第一个、最后一个、前一个、后一个:
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class MyPanel extends JPanel {
int x;
JLabel label1;
MyPanel(int a) {
x = a;
getSize();
label1 = new JLabel("我是第" + x + "个标签");
add(label1);
}
public Dimension getPreferredSize() {
return new Dimension(200, 50);
}
}
public class J507 extends Applet implements ActionListener {
CardLayout mycard;
MyPanel myPanel[];
JPanel p;
private void addButton(JPanel pan, String butName, ActionListener listener) {
JButton aButton = new JButton(butName);
aButton.addActionListener(listener);
pan.add(aButton);
}
public void init() {
setLayout(new BorderLayout()); // 小程序的布局是边界布局
mycard = new CardLayout();
this.setSize(400, 150);
p = new JPanel();
p.setLayout(mycard); // p 的布局设置为卡片式布局
myPanel = new MyPanel[10];
for (int i = 0; i < 10; i++) {
myPanel[i] = new MyPanel(i + 1);
p.add("A" + i, myPanel[i]);
}
JPanel p2 = new JPanel();
addButton(p2, "第一个", this);
addButton(p2, "最后一个", this);
addButton(p2, "前一个", this);
addButton(p2, "后一个", this);
add(p, "Center");
add(p2, "South");
}
public void actionPerformed(ActionEvent e) {
if (e.getActionCommand().equals("第一个")) mycard.first(p);
else if (e.getActionCommand().equals("最后一个")) mycard.last(p);
else if (e.getActionCommand().equals("前一个")) mycard.previous(p);
else if (e.getActionCommand().equals("后一个")) mycard.next(p);
}
}
五、null 布局与 setBounds
空布局就是把容器布局设为 null。用 setBounds(int x, int y, int width, int height) 设置组件大小和位置:组件区域是矩形,x、y 是左上角坐标,width、height 是宽和高。步骤:先 add() 添加组件,再 setBounds()。相关方法:
getSize().width
getSize().height
setVgap(int vgap)
setHgap(int hgap);
| 布局 |
默认用在 |
特点 |
| FlowLayout |
JApplet / JPanel / JScrollPane |
从左到右流式,一行满换行 |
| BorderLayout |
JWindow / JFrame / JDialog |
东南西北中五区,一区一组件 |
| GridLayout |
需整齐网格时 |
行列等大格子,行数固定列可增减 |
| CardLayout |
向导、选项卡式切换 |
同一空间一次只显示一个组件 |
| null + setBounds |
绝对定位 |
自己算坐标,窗口缩放不会自动重排 |
一句话总结:容器靠布局管理器摆组件:流式、边界、网格、卡片各管一类场景;精细到像素再用 null 加 setBounds,嵌套容器可以混用多种策略。