14.3 布局管理器
14.2 留下的谜底揭晓:组件为什么"自己排队"?因为每个容器(JFrame/JPanel)出厂自带一位布局管理器——隐藏的排版师傅。本节认识三位常客:FlowLayout(流式,JPanel 默认)、BorderLayout(边界,JFrame 默认)、GridLayout(网格),以及换师傅的开关 setLayout。师傅们都在 java.awt 包里(14.1 的老东家)。
一、谜底揭晓:谁在偷偷排版?
上节 Demo27 里,组件没写一句坐标代码,却排得整整齐齐——不是组件自己懂事,是容器里蹲着一位排版师傅,每 add 一个组件,师傅就按自己的规矩给它安排位置。
- 布局管理器是容器的属性,不是组件的。
setLayout永远调在 JFrame/JPanel 这些容器身上。 - 想换排法,就换个师傅:
容器.setLayout(new 某布局()); - 三位常客的"默认岗位"先背下来:
| 布局 | 类(java.awt 包) | 排法 | 谁默认用它 |
|---|---|---|---|
| 流式布局 | FlowLayout |
按 add 顺序排队,排满换行 | JPanel |
| 边界布局 | BorderLayout |
东南西北中五个占位区 | JFrame |
| 网格布局 | GridLayout |
N 行 × M 列,格子均分 | 无(手动指定) |
二、FlowLayout:像打字一样换行
打字的规矩就是它的规矩:从左到右排,一行装不下就换下一行,窗口拉窄了还会自动重新换行。
JPanel panel = new JPanel(); // JPanel 出厂自带 FlowLayout
JPanel panel2 = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 20));
// 构造参数:对齐方式(默认居中)、水平间距、垂直间距
- 优点:省心,随便 add 都不乱。
- 缺点:窗口一拉伸,组件跟着挪窝,摆不出"标签左、输入框右"的表格感——所以 14.2 才说"想摆整齐要靠布局"。
三、BorderLayout:东南西北中,五个占位区
这是 JFrame 的默认师傅,把窗口切成五块地皮:
frame.add(btn, BorderLayout.NORTH); // 北:顶上一条
frame.add(btn2, BorderLayout.SOUTH); // 南:底下一长条
frame.add(btn3, BorderLayout.EAST); // 东:右边竖条
frame.add(btn4, BorderLayout.WEST); // 西:左边竖条
frame.add(btn5, BorderLayout.CENTER); // 中:剩下的全部
两条铁规矩:
- add 不给位置,默认进中区——
frame.add(comp)等价于frame.add(comp, BorderLayout.CENTER)。 - 一个区只站一个组件,后来的顶走先来的——往北区 add 三次,前两次的组件直接消失。
上节 Demo27 为什么显示正常?因为组件全 add 进了 JPanel(自带 FlowLayout 排成一排),再整块板子 add 进 frame 的中区——一套娃救了一命。要是直接往 JFrame 上 add 六个组件,它们会全挤进中区叠罗汉,最后只剩一个。 这就是 14.1 套娃三层(窗口→面板→组件)真正的用意:面板是打包运输的快递箱,让每个区能装下"一组"组件。
四、GridLayout:整齐的表格格子
JPanel calc = new JPanel(new GridLayout(2, 3, 5, 5)); // 2 行 3 列,格子间水平/垂直缝 5px
calc.add(new JButton("7")); // 按顺序填格:先填满第一行,再填第二行
calc.add(new JButton("8"));
calc.add(new JButton("9"));
calc.add(new JButton("4"));
calc.add(new JButton("5"));
calc.add(new JButton("6"));
- 行在前、列在后:
GridLayout(行, 列)——写反了就摆成 2 列 3 行。 - 每个格子大小强制均分,组件塞进去自动撑满——计算器按键、棋盘的标配。
- 一个格子一个组件,想塞多个?老办法:JPanel 打包。
五、动手:Demo28 排版版注册表单
把 14.2 的 Demo27 从"一排流"升级成上下分区:
import javax.swing.*;
import java.awt.*;
public class Demo28 {
public static void main(String[] args) {
JFrame frame = new JFrame("注册新账号 · 排版版");
frame.setSize(360, 200);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel north = new JPanel(); // 北区板子:自带 FlowLayout
north.add(new JLabel("用户名"));
north.add(new JTextField(20));
JPanel center = new JPanel(new GridLayout(2, 2, 5, 5)); // 2行2列网格
center.add(new JLabel("密码"));
center.add(new JPasswordField(20));
center.add(new JCheckBox("我同意注册协议"));
center.add(new JButton("注册"));
frame.add(north, BorderLayout.NORTH); // 北区:整块板子上岗
frame.add(center, BorderLayout.CENTER); // 中区:整块板子上岗
frame.setVisible(true);
}
}
跑起来:用户名一行在顶上,密码、协议、注册按钮排成整齐的 2×2 网格——还是那些组件,只是换了个师傅、多打了一次包。
不建 JPanel,frame.add(new JButton("A")); frame.add(new JButton("B")); frame.add(new JButton("C"));——窗口里只剩按钮 C。因为 JFrame 默认 BorderLayout,三个全进了中区,B 顶走 A,C 顶走 B。
在实验 1 基础上加一句 frame.setLayout(new FlowLayout());——A、B、C 全回来了,还排成一排。换师傅,立刻见效。
跑 Demo28 后把窗口横向拉窄:北区的"用户名"标签和输入框会自动换行——流式布局的"打字规矩"是活的,跟着窗口宽度走。
六、五个常见误区
布局是容器的属性:panel.setLayout(...) 对,btn.setLayout(...) 没意义。先想清楚"我在给谁排版"。
默认 BorderLayout 全进中区、后到顶先到,结果"只剩最后一个"。要么给每个组件指定区域,要么先打包进 JPanel。
BorderLayout 一个区只认一个组件。想放多个:先装进 JPanel(自带 FlowLayout),整块板子再 add 进那个区。
GridLayout(rows, cols) 行在前列在后。填 0 表示"这一维自动算"(如 new GridLayout(0, 3) = 每行 3 个、行数随组件数涨),但两个都写 0 直接报异常。
绝对定位要自己 setBounds(x, y, 宽, 高) 一个个摆,窗口一拉伸全乱套。本阶段认识即可,别在练习里用。
七、自测题
FlowLayout 流式排队、排满换行 → JPanel 默认;BorderLayout 东南西北中五区 → JFrame 默认;GridLayout 行列均分格子 → 手动指定。
一个。默认 BorderLayout 全进中区,一个区只站一个组件,后来的顶走先来的。
BorderLayout 一个区只放一个组件,想放一组就先用 JPanel 打包,整块板子当一个组件 add。Demo27 的组件全在 JPanel(流式排好)里,再整板进中区,所以没叠。
每行 3 个格子,行数自动按组件数量算(0 = 该维度自动)。行列都写 0 会抛异常。
参考骨架:
JPanel north = new JPanel();
north.add(new JLabel("用户名")); north.add(new JTextField(20));
JPanel center = new JPanel(new GridLayout(2, 2));
center.add(new JLabel("密码")); center.add(new JPasswordField(20));
center.add(new JLabel("确认密码")); center.add(new JPasswordField(20));
JPanel south = new JPanel();
south.add(new JCheckBox("我同意注册协议")); south.add(new JButton("注册"));
frame.add(north, BorderLayout.NORTH);
frame.add(center, BorderLayout.CENTER);
frame.add(south, BorderLayout.SOUTH);
自检:每块板子自带 FlowLayout 所以板内自动排队、板与板之间靠 BorderLayout 分区——两层师傅配合干活。界面彻底成型,下一步让它"活"过来。
八、下一步学什么
- ☑ 14.4 事件处理机制 ✅ —— 点此复习:事件三要素、addActionListener=装门铃、三种写法演进到 Lambda、Demo29 注册表单活了
- ☐ 14.5(按教材顺序) —— 报个标题接着走。等你报标题。
相关笔记
- 第十四章 图形用户界面/14.2 Swing的常见组件:本节排版的居民——三大家族组件
- 第十四章 图形用户界面/14.1 Swing概述:套娃三层的用意本节正式揭晓——面板是打包的快递箱
- 第四章 面向对象(下)/4.3 抽象类和接口:布局管理器底层也是接口/抽象体系,师傅们各有各的排法