☕ 第十四章 图形用户界面

14.3 布局管理器

📄 一句话概括

14.2 留下的谜底揭晓:组件为什么"自己排队"?因为每个容器(JFrame/JPanel)出厂自带一位布局管理器——隐藏的排版师傅。本节认识三位常客:FlowLayout(流式,JPanel 默认)、BorderLayout(边界,JFrame 默认)、GridLayout(网格),以及换师傅的开关 setLayout。师傅们都在 java.awt 包里(14.1 的老东家)。


一、谜底揭晓:谁在偷偷排版?

上节 Demo27 里,组件没写一句坐标代码,却排得整整齐齐——不是组件自己懂事,是容器里蹲着一位排版师傅,每 add 一个组件,师傅就按自己的规矩给它安排位置。

布局 类(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));
// 构造参数:对齐方式(默认居中)、水平间距、垂直间距

三、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); // 中:剩下的全部

两条铁规矩:

  1. add 不给位置,默认进中区——frame.add(comp) 等价于 frame.add(comp, BorderLayout.CENTER)。
  2. 一个区只站一个组件,后来的顶走先来的——往北区 add 三次,前两次的组件直接消失。
💡 Demo27 之谜收网

上节 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"));

五、动手: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 网格——还是那些组件,只是换了个师傅、多打了一次包。

🧪 实验 1:直接往 JFrame 上 add 三个按钮

不建 JPanel,frame.add(new JButton("A")); frame.add(new JButton("B")); frame.add(new JButton("C"));——窗口里只剩按钮 C。因为 JFrame 默认 BorderLayout,三个全进了中区,B 顶走 A,C 顶走 B。

🧪 实验 2:给 JFrame 换个流式师傅

在实验 1 基础上加一句 frame.setLayout(new FlowLayout());——A、B、C 全回来了,还排成一排。换师傅,立刻见效。

🧪 实验 3:拉伸窗口看 FlowLayout 换行

跑 Demo28 后把窗口横向拉窄:北区的"用户名"标签和输入框会自动换行——流式布局的"打字规矩"是活的,跟着窗口宽度走。

六、五个常见误区

⚠️ 误区 1:把 setLayout 调在组件身上

布局是容器的属性:panel.setLayout(...) 对,btn.setLayout(...) 没意义。先想清楚"我在给谁排版"。

⚠️ 误区 2:往 JFrame 直接 add 一堆组件

默认 BorderLayout 全进中区、后到顶先到,结果"只剩最后一个"。要么给每个组件指定区域,要么先打包进 JPanel。

⚠️ 误区 3:一个区 add 两次想放两个组件

BorderLayout 一个区只认一个组件。想放多个:先装进 JPanel(自带 FlowLayout),整块板子再 add 进那个区。

⚠️ 误区 4:GridLayout 的行列参数写反

GridLayout(rows, cols) 行在前列在后。填 0 表示"这一维自动算"(如 new GridLayout(0, 3) = 每行 3 个、行数随组件数涨),但两个都写 0 直接报异常。

⚠️ 误区 5:setLayout(null) 手动摆坐标

绝对定位要自己 setBounds(x, y, 宽, 高) 一个个摆,窗口一拉伸全乱套。本阶段认识即可,别在练习里用。

七、自测题

❓ Q1:三位常客布局各是什么排法?谁是谁的默认师傅?

FlowLayout 流式排队、排满换行 → JPanel 默认;BorderLayout 东南西北中五区 → JFrame 默认;GridLayout 行列均分格子 → 手动指定。

❓ Q2:往 JFrame 上 add 三个组件不给位置,最后看到几个?

一个。默认 BorderLayout 全进中区,一个区只站一个组件,后来的顶走先来的。

❓ Q3:为什么"面板是快递箱"?Demo27 靠什么躲过了叠罗汉?

BorderLayout 一个区只放一个组件,想放一组就先用 JPanel 打包,整块板子当一个组件 add。Demo27 的组件全在 JPanel(流式排好)里,再整板进中区,所以没叠。

❓ Q4:new GridLayout(0, 3) 是什么意思?

每行 3 个格子,行数自动按组件数量算(0 = 该维度自动)。行列都写 0 会抛异常。

❓ Q5:(毕业题)把 14.2 的注册表单完整排版:用户名一行在北区,密码+确认密码 2×2 网格在中区,协议勾选和注册按钮一排在南区。

参考骨架:

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 分区——两层师傅配合干活。界面彻底成型,下一步让它"活"过来。

八、下一步学什么


相关笔记