目录
- 核心概念:什么是 Swing?
- 第一个 Swing 程序:Hello World
- Swing 架构:MVC 设计模式
- 常用 Swing 组件详解
- 容器类
- 基础组件
- 布局管理器
- 事件处理
- 高级主题
- 多线程与
SwingWorker - 自定义绘制
- 多线程与
- 开发工具与最佳实践
- 总结与学习资源
核心概念:什么是 Swing?
Swing 是 Java 基础类库的一部分,它提供了一套丰富的 GUI 组件(如按钮、文本框、菜单等),它的主要特点包括:

- 轻量级组件:Swing 组件是用纯 Java 编写的,不依赖于本地操作系统的 GUI 元素,这使得 Swing 应用程序在不同平台上具有一致的外观和感觉。
- 可插拔外观:你可以轻松地更改应用程序的外观,使其看起来像 Windows、macOS、GTK 或使用 Java 自身的 Metal 外观。
- 丰富的事件模型:通过监听器机制,可以响应用户的各种操作(如点击、输入、选择等)。
- 强大的组件库:从简单的按钮到复杂的表格、树和文本编辑器,Swing 提供了几乎所有 GUI 开发所需的基础组件。
第一个 Swing 程序:Hello World
让我们从一个最简单的窗口开始,理解创建一个 Swing 应用程序的基本步骤。
import javax.swing.*; // 导入 Swing 的所有核心类
public class HelloWorldSwing {
// 1. 创建并显示 GUI,为了线程安全,这个方法应该在事件分发线程上调用。
private static void createAndShowGUI() {
// 2. 创建一个顶级窗口(JFrame)
JFrame frame = new JFrame("HelloWorldSwing");
// 3. 当用户关闭窗口时,程序退出
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 4. 添加一个标签到窗口中
JLabel label = new JLabel("Hello, World!");
frame.getContentPane().add(label);
// 5. 调整窗口大小以适应其内容
frame.pack();
// 6. 让窗口在屏幕上可见
frame.setVisible(true);
}
public static void main(String[] args) {
// 7. 在事件分发线程上创建和显示 GUI
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
代码解释:
- *`import javax.swing.;`**: 导入 Swing 包,这是所有 Swing 组件的来源。
JFrame frame = new JFrame("HelloWorldSwing");: 创建一个JFrame实例,这是应用程序的主窗口,字符串是窗口的标题。frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);: 设置窗口的默认关闭操作,如果不设置,关闭窗口只是隐藏它,程序后台进程仍在运行。EXIT_ON_CLOSE表示关闭窗口时退出整个应用程序。JLabel label = new JLabel("Hello, World!");: 创建一个JLabel(标签)组件,用于显示静态文本。frame.getContentPane().add(label);: 获取JFrame的内容面板,并将标签添加到其中,所有组件都必须添加到一个容器中。frame.pack();: 自动调整窗口的大小,使其刚好能容纳所有已添加的组件。frame.setVisible(true);: 将窗口设置为可见,在此之前,窗口是不可见的。SwingUtilities.invokeLater(...): 这是 Swing 编程的黄金法则! 所有与 GUI 创建和更新相关的操作都必须在 事件分发线程 上执行。invokeLater方法确保createAndShowGUI()中的代码会在 EDT 上安全地运行,避免了多线程操作 GUI 可能导致的线程安全问题。
Swing 架构:MVC 设计模式
许多 Swing 组件都采用了 Model-View-Controller (MVC) 设计模式:
- Model (模型): 负责存储数据。
JList的模型是一个ListModel,它包含了列表中的所有数据。 - View (视图): 负责数据的可视化呈现。
JList组件本身就是视图,它根据模型数据显示出列表。 - Controller (控制器): 负责处理用户输入并更新模型,当用户选择列表中的某一项时,控制器会更新模型的选中状态,视图会随之重新绘制。
这种模式的好处是 数据与显示分离,你可以改变数据(模型),而视图会自动更新;或者你可以改变组件的外观(视图),而数据(模型)保持不变。

常用 Swing 组件详解
A. 容器类
容器是用来放置其他组件的组件。
JFrame: 顶级窗口,通常作为应用程序的主窗口。JPanel: 通用轻量级容器,它没有边框,常用于对其他组件进行分组或作为自定义绘制的画布。JDialog: 模态或非模态的对话框窗口。JScrollPane: 提供滚动条的面板,当其内部组件过大时,可以通过滚动条查看。
B. 基础组件
JLabel: 显示文本或图像。JButton: 点击按钮触发动作。JTextField: 单行文本输入框。JTextArea: 多行文本区域,不支持自动换行(除非放在JScrollPane中)。JPasswordField: 密码输入框,输入的字符会显示为掩码(如 )。JCheckBox: 复选框,可多选。JRadioButton: 单选按钮,需要与ButtonGroup配合使用以确保同一组内只有一个被选中。JComboBox: 下拉列表框。JList: 列表组件,可以显示一列项目。JTable: 表格组件,用于显示二维数据。JTree: 树形组件,用于展示分层数据。
C. 布局管理器
布局管理器负责决定容器中组件的大小和位置。绝对定位(null 布局) 在复杂的 GUI 中很难维护,因此强烈建议使用布局管理器。
BorderLayout: 将容器分为五个区域:NORTH,SOUTH,EAST,WEST,CENTER。JFrame的默认布局。FlowLayout: 将组件从左到右、从上到下依次排列,像文字一样。JPanel的默认布局。GridLayout: 将容器划分为一个网格,每个组件占据一个格子。BoxLayout: 允许组件在垂直或水平方向上依次排列。GridBagLayout: 最强大也最复杂的布局管理器,允许组件跨越多行多列,并提供精细的大小和对齐控制。
示例:使用 BorderLayout 和 FlowLayout
import javax.swing.*;
import java.awt.*;
public class LayoutExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("布局示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 200);
// 使用 BorderLayout 作为主框架的布局
frame.setLayout(new BorderLayout(5, 5)); // 水平和垂直间距为5像素
// 顶部面板,使用 FlowLayout
JPanel topPanel = new JPanel(new FlowLayout());
topPanel.add(new JButton("按钮 1"));
topPanel.add(new JButton("按钮 2"));
frame.add(topPanel, BorderLayout.NORTH);
// 中间面板
JLabel centerLabel = new JLabel("这是中心区域", SwingConstants.CENTER);
frame.add(centerLabel, BorderLayout.CENTER);
// 底部面板
JPanel bottomPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
bottomPanel.add(new JButton("确定"));
bottomPanel.add(new JButton("取消"));
frame.add(bottomPanel, BorderLayout.SOUTH);
frame.setVisible(true);
});
}
}
事件处理
Swing 使用 监听器 模式来处理事件,基本流程是:
- 获取事件源:比如一个
JButton。 - 创建监听器对象:实现一个特定的监听器接口(如
ActionListener)。 - 将监听器注册到事件源:调用事件源的
addXXXListener()方法。
示例:按钮点击事件
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class EventExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("事件处理示例");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
JButton clickButton = new JButton("点击我");
JLabel statusLabel = new JLabel("等待点击...");
// 1. 创建监听器对象(匿名内部类)
ActionListener buttonListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 当按钮被点击时,此方法会被调用
statusLabel.setText("按钮被点击了!");
System.out.println("按钮在事件源: " + e.getSource() + " 上被点击。");
}
};
// 2. 将监听器注册到按钮
clickButton.addActionListener(buttonListener);
frame.add(clickButton);
frame.add(statusLabel);
frame.pack();
frame.setVisible(true);
});
}
}
高级主题
A. 多线程与 SwingWorker
重要原则: 永远不要在事件分发线程 上执行耗时操作(如网络请求、文件读写、复杂计算),否则,GUI 会冻结,无法响应用户输入。
解决方案是使用 SwingWorker,它是一个抽象类,专门用于在后台线程中执行耗时任务,并在任务完成后安全地更新 GUI。
SwingWorker 的工作流程:
- 在
doInBackground()方法中执行耗时任务。 - 在
process()方法中(可选),可以定期发布中间结果并更新 GUI。 - 在
done()方法中,在任务完成后更新 GUI。
B. 自定义绘制
当标准组件无法满足你的需求时(如绘制自定义图形、图表、游戏),你可以通过继承 JComponent 或 JPanel 并重写其 paintComponent(Graphics g) 方法来实现自定义绘制。
关键点:
- 始终调用
super.paintComponent(g);:这是为了保证 Swing 能够正确地绘制组件的背景等。 - 在
paintComponent中进行所有绘制:不要在构造函数或其他方法中直接绘制,因为该方法会在组件需要重绘时被自动调用(如窗口大小改变、被其他窗口遮挡后重新显示)。
import javax.swing.*;
import java.awt.*;
public class CustomDrawing extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // 必须首先调用
// 将 Graphics 对象转换为更强大的 Graphics2D
Graphics2D g2d = (Graphics2D) g;
// 设置抗锯齿,使图形更平滑
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// 绘制一个红色填充的矩形
g2d.setColor(Color.RED);
g2d.fillRect(50, 50, 100, 80);
// 绘制一个蓝色边框的椭圆
g2d.setColor(Color.BLUE);
g2d.setStroke(new BasicStroke(3)); // 设置线条宽度
g2d.drawOval(200, 50, 100, 80);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("自定义绘制");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CustomDrawing());
frame.setSize(400, 200);
frame.setVisible(true);
});
}
}
开发工具与最佳实践
- 开发工具:
- IDE (集成开发环境):IntelliJ IDEA 和 Eclipse 都对 Swing 开发提供了非常好的支持,包括可视化界面设计器(如 IntelliJ 的 GUI Designer)。
- 可视化设计器:可以像拖拽控件一样快速构建界面,并自动生成代码,非常适合初学者和快速原型开发。
- 最佳实践:
- 始终使用
SwingUtilities.invokeLater:创建和更新 GUI 的唯一入口。 - 使用布局管理器:避免使用绝对定位,让你的 GUI 具有更好的可移植性和可维护性。
- 分离逻辑与视图:将业务逻辑代码与 GUI 创建代码分离开,使用 MVC 模式或至少将它们放在不同的类中。
- 为组件命名:给按钮、文本框等组件起有意义的名字,便于管理和调试。
- 使用
Action接口:对于菜单项和工具栏按钮,使用Action接口可以让你将一个动作与一个图标、文本和快捷键绑定在一起,并在多处复用。
- 始终使用
总结与学习资源
Java Swing 是一个成熟且功能强大的 GUI 工具包,虽然它的 API 有些古老,但其核心思想(事件驱动、MVC)至今仍是 GUI 编程的基础。
学习资源:
- 官方文档:Java Swing Tutorial (Oracle) - 最权威、最全面的教程。
- 书籍:《Java Swing (David Geary 和 James Elliott 著)》是经典的 Swing 参考书。
- 在线教程:像 Baeldung 和 Madhusudan's Blog 等网站有很多实用的 Swing 教程和示例代码。
- 开源项目:在 GitHub 上寻找使用 Swing 开发的开源项目,阅读源码是提升技能的绝佳方式。
从创建一个简单的窗口开始,逐步尝试添加不同的组件、布局和事件处理,你很快就能掌握 Java Swing 程序设计的精髓,祝你编程愉快!
