目录
- Swing 核心概念
- AWT vs. Swing
- Swing 的层次结构:顶级容器、中间容器、组件
- 布局管理器
- 事件处理
- Swing 开发环境准备
- 创建第一个简单的Swing应用
- 常用Swing组件详解
- 顶级容器
- 常用UI组件
- 菜单和工具栏
- 布局管理器详解
FlowLayoutBorderLayoutGridLayoutBoxLayoutGridBagLayout(最强大也最复杂)- 组合使用布局管理器
- 事件处理机制
- 事件监听器
- 匿名内部类写法
- Lambda表达式 (Java 8+)
- 高级技巧
- MVC (Model-View-Controller) 设计模式
- 使用
Action接口 - 自定义绘制
- 一个完整的项目示例:简易计算器
- 推荐资源
Swing 核心概念
AWT vs. Swing
- AWT (Abstract Window Toolkit): Java最早的GUI工具包,它使用操作系统的原生组件,因此在不同平台上的外观和行为可能不一致,它被称为“重量级”组件。
- Swing: 在AWT基础上构建,它使用纯Java绘制,不依赖操作系统的GUI组件,因此外观更统一,并且功能更丰富,它被称为“轻量级”组件。在现代Java开发中,我们几乎总是优先选择Swing。
Swing 的层次结构
Swing组件的组织像一个家族树:
- 顶级容器: 可以独立存在的窗口。
JFrame,JDialog,JApplet。 - 中间容器: 用于组织和布局其他组件。
JPanel,JScrollPane,JSplitPane,JTabbedPane。 - 基本组件: 用户与之交互的UI元素。
JButton,JLabel,JTextField,JTextArea,JTable。
所有Swing组件都以 "J" 开头,以区别于AWT组件。
布局管理器
布局管理器负责决定组件在容器中的位置和大小。绝对定位(直接设置setBounds(x, y, width, height))在Swing中强烈不推荐,因为它会导致界面在不同分辨率或窗口大小下变得一团糟。
Swing提供了多种内置的布局管理器,你需要根据需求选择或组合使用它们。
事件处理
Swing是事件驱动的,用户的操作(如点击按钮、输入文本)会产生一个“事件”,你需要为组件注册一个“事件监听器”来响应这些事件,为按钮添加一个ActionListener,当按钮被点击时,actionPerformed方法就会被执行。
Swing 开发环境准备
你只需要一个标准的Java开发环境:
- JDK (Java Development Kit): 确保已安装并配置好
JAVA_HOME和PATH环境变量。 - IDE (集成开发环境): 推荐使用 IntelliJ IDEA 或 Eclipse,它们都提供了强大的Swing可视化设计器(如 IntelliJ 的 GUI Designer),可以让你通过拖拽的方式快速构建界面,但强烈建议初学者先通过手写代码来理解Swing的工作原理。
创建第一个简单的Swing应用
一个最简单的Swing程序必须包含一个JFrame窗口。
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
public class FirstSwingApp {
public static void main(String[] args) {
// 使用 SwingUtilities.invokeLater 确保 GUI 创建在事件分发线程上
SwingUtilities.invokeLater(() -> {
// 1. 创建顶级容器 (窗口)
JFrame frame = new JFrame("我的第一个Swing窗口");
// 2. 设置窗口行为
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 关闭窗口时退出程序
frame.setSize(400, 300); // 设置窗口大小
frame.setLocationRelativeTo(null); // 窗口居中显示
// 3. 创建组件并添加到窗口
JLabel label = new JLabel("你好,Swing世界!", JLabel.CENTER);
frame.add(label);
// 4. 让窗口可见
frame.setVisible(true);
});
}
}
代码解释:
SwingUtilities.invokeLater(...): 这是Swing编程的黄金法则,所有与GUI创建和更新的代码都应该在事件分发线程上执行。invokeLater确保了这一点。JFrame: 窗口对象。setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE): 定义窗口关闭时的默认操作,没有这行,关闭窗口后程序进程可能仍在后台运行。setSize(): 设置窗口的初始宽度和高度(像素)。setLocationRelativeTo(null): 让窗口在屏幕中央显示。JLabel: 用于显示文本或图像的标签组件。frame.add(label): 将组件添加到窗口的默认内容面板中。setVisible(true): 窗口默认是不可见的,必须调用此方法才能显示。
常用Swing组件详解
顶级容器
JFrame: 主窗口,通常包含标题栏、菜单栏、边框和一个内容面板。JDialog: 对话框窗口,通常用于显示临时信息或接收用户输入,它是模态(阻塞)或非模态的。
常用UI组件
JButton: 按钮。JLabel: 标签,用于显示不可编辑的文本或图标。JTextField: 单行文本输入框。JPasswordField: 密码输入框,输入的字符会以遮蔽符显示。JTextArea: 多行文本区域,不支持自动换行(除非配合JScrollPane)。JComboBox: 下拉列表框。JList: 列表框,可以显示一个字符串列表供用户选择。JCheckBox: 复选框,可以选中或取消选中。JRadioButton: 单选按钮,需要与ButtonGroup配合使用,确保一组中只有一个被选中。JSlider: 滑块,用于在指定范围内选择一个值。
菜单和工具栏
JMenuBar: 菜单栏,通常位于窗口顶部。JMenu: 菜单,如“文件”、“编辑”。JMenuItem: 菜单项,是菜单中的具体选项。JToolBar: 工具栏,包含常用操作按钮。
布局管理器详解
FlowLayout
组件从左到右、从上到下依次排列,像流水一样,默认情况下,组件居中显示。
JPanel panel = new JPanel();
panel.setLayout(new FlowLayout()); // 默认居中
panel.setLayout(new FlowLayout(FlowLayout.LEFT)); // 左对齐
panel.add(new JButton("按钮1"));
panel.add(new JButton("一个很长的按钮2"));
panel.add(new JButton("按钮3"));
BorderLayout
将容器划分为五个区域:NORTH (北), SOUTH (南), WEST (西), EAST (东), CENTER (中心),每个区域只能添加一个组件。CENTER区域会自动填充剩余空间。JFrame的默认布局就是BorderLayout。
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JButton("北"), BorderLayout.NORTH);
panel.add(new JButton("南"), BorderLayout.SOUTH);
panel.add(new JButton("西"), BorderLayout.WEST);
panel.add(new JButton("东"), BorderLayout.EAST);
panel.add(new JButton("中间区域会占据所有剩余空间"), BorderLayout.CENTER);
GridLayout
将容器划分为一个网格,每个组件占据一个格子,所有格子大小相同。
// 3行2列,组件之间水平间距5,垂直间距5
JPanel panel = new JPanel(new GridLayout(3, 2, 5, 5));
panel.add(new JLabel("用户名:"));
panel.add(new JTextField());
panel.add(new JLabel("密码:"));
panel.add(new JPasswordField());
panel.add(new JButton("登录"));
panel.add(new JButton("取消"));
BoxLayout
允许组件在垂直或水平方向上一字排开,它通常与JPanel一起使用。
// 垂直排列
JPanel vPanel = new JPanel();
vPanel.setLayout(new BoxLayout(vPanel, BoxLayout.Y_AXIS));
vPanel.add(new JButton("按钮1"));
vPanel.add(new JButton("按钮2"));
vPanel.add(new JButton("按钮3"));
// 水平排列
JPanel hPanel = new JPanel();
hPanel.setLayout(new BoxLayout(hPanel, BoxLayout.X_AXIS));
hPanel.add(new JButton("A"));
hPanel.add(new JButton("B"));
hPanel.add(new JButton("C"));
GridBagLayout (最强大也最复杂)
这是最灵活、最强大的布局管理器,但也最复杂,它允许组件跨越多个行和列,并可以精确控制组件的对齐方式和填充方式。
使用GridBagLayout通常需要配合GridBagConstraints对象来设置每个组件的约束。
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// 第一个组件
gbc.gridx = 0;
gbc.gridy = 0;
gbc.gridwidth = 1; // 占1列
gbc.gridheight = 1; // 占1行
gbc.fill = GridBagConstraints.HORIZONTAL; // 水平填充
panel.add(new JLabel("用户名:"), gbc);
// 第二个组件
gbc.gridx = 1;
gbc.gridy = 0;
gbc.gridwidth = 2; // 占2列
gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(new JTextField(15), gbc);
// 第三个组件
gbc.gridx = 0;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.fill = GridBagConstraints.NONE;
panel.add(new JLabel("密码:"), gbc);
// 第四个组件
gbc.gridx = 1;
gbc.gridy = 1;
gbc.gridwidth = 2;
gbc.fill = GridBagConstraints.HORIZONTAL;
panel.add(new JPasswordField(15), gbc);
组合使用布局管理器
在实际开发中,最常见的方式是嵌套使用JPanel和不同的布局管理器。
一个窗口使用BorderLayout,将工具栏放在NORTH区放在CENTER区用一个JPanel,使用GridLayout来排列输入表单,而表单中的按钮区域又用一个FlowLayout的JPanel来放置。
事件处理机制
事件监听器
以按钮点击为例:
- 创建一个实现
ActionListener接口的类。 - 实现
actionPerformed(ActionEvent e)方法。 - 使用
button.addActionListener(new MyListener())将监听器注册到按钮。
匿名内部类写法
这是最常见的写法,无需单独定义一个类。
JButton button = new JButton("点击我");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击了!");
// 在这里执行点击后的逻辑,例如更新标签文本
label.setText("你好,世界!");
}
});
Lambda表达式 (Java 8+)
Lambda表达式让事件处理代码更简洁。
JButton button = new JButton("点击我");
button.addActionListener(e -> {
System.out.println("按钮被点击了!");
label.setText("你好,Lambda世界!");
});
e -> { ... } 是 ActionListener 接口中 actionPerformed 方法的简写形式。
高级技巧
MVC (Model-View-Controller) 设计模式
对于复杂的GUI应用,遵循MVC模式是一个好习惯:
- Model (模型): 负责数据和业务逻辑,一个用户类,包含用户名和密码。
- View (视图): 负责显示界面,即所有的Swing组件。
- Controller (控制器): 负责接收用户输入,并更新模型和视图,按钮的
ActionListener。
当模型数据改变时,控制器会通知视图进行更新,从而实现数据和UI的分离。
使用 Action 接口
当你希望多个控件(如一个菜单项和一个工具栏按钮)执行相同的操作时,可以使用Action接口,你可以创建一个AbstractAction子类,定义actionPerformed方法,然后将这个Action对象同时设置给菜单项和按钮。
自定义绘制
Swing组件允许你覆盖其paintComponent方法来实现自定义绘制,这在制作图表、游戏或特殊效果时非常有用。
class MyCustomPanel extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // 必须先调用父类方法
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillRect(50, 50, 100, 100);
}
}
一个完整的项目示例:简易计算器
这个例子将综合运用前面所学的知识:JFrame, JPanel, GridLayout, FlowLayout, 事件处理。
目标: 创建一个可以进行加减乘除的计算器界面。
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SimpleCalculator extends JFrame {
private JTextField displayField;
private double result = 0;
private String lastCommand = "=";
private boolean start = true;
public SimpleCalculator() {
// 1. 初始化窗口
setTitle("简易计算器");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 400);
setLocationRelativeTo(null);
setLayout(new BorderLayout(5, 5)); // 主窗口使用BorderLayout
// 2. 创建显示区域
displayField = new JTextField("0");
displayField.setEditable(false);
displayField.setHorizontalAlignment(JTextField.RIGHT);
displayField.setFont(new Font("Arial", Font.BOLD, 24));
add(displayField, BorderLayout.NORTH);
// 3. 创建按钮面板,使用GridLayout
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(5, 4, 5, 5)); // 5行4列
// 4. 创建按钮并添加到面板
String[] buttonLabels = {
"7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+",
"C", "CE", "⌫", "±"
};
for (String label : buttonLabels) {
JButton button = new JButton(label);
button.setFont(new Font("Arial", Font.BOLD, 18));
button.addActionListener(new ButtonClickListener());
buttonPanel.add(button);
}
// 5. 将按钮面板添加到窗口
add(buttonPanel, BorderLayout.CENTER);
// 6. 添加边距,让界面更美观
((JComponent)getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
}
// 内部类,作为按钮的事件监听器
private class ButtonClickListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.matches("[0-9.]")) {
// 处理数字输入
if (start) {
displayField.setText("");
start = false;
}
if (command.equals(".") && displayField.getText().contains(".")) {
return; // 防止输入多个小数点
}
displayField.setText(displayField.getText() + command);
} else {
// 处理命令
if (!start) {
calculate(command);
}
lastCommand = command;
start = true;
}
}
private void calculate(String command) {
double x = Double.parseDouble(displayField.getText());
double y = result;
switch (lastCommand) {
case "+":
result = x + y;
break;
case "-":
result = y - x;
break;
case "*":
result = x * y;
break;
case "/":
if (x == 0) {
displayField.setText("Error");
result = 0;
return;
}
result = y / x;
break;
case "=":
result = x;
break;
case "C": // Clear
case "CE": // Clear Entry
result = 0;
displayField.setText("0");
start = true;
return;
case "⌫": // Backspace
String text = displayField.getText();
if (text.length() > 1) {
displayField.setText(text.substring(0, text.length() - 1));
} else {
displayField.setText("0");
}
return;
case "±": // Plus/Minus
result = -x;
displayField.setText(String.valueOf(result));
start = true;
return;
}
displayField.setText(String.valueOf(result));
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SimpleCalculator calculator = new SimpleCalculator();
calculator.setVisible(true);
});
}
}
推荐资源
- Oracle官方文档: Java Swing Tutorial (Oracle) - 最权威、最全面的资料。
- 书籍:
- 《Core Java Volume I—Fundamentals》 (by Cay S. Horstmann) - 经典Java教材,Swing部分讲解得非常透彻。
- 《Swing: A Beginner's Guide》 (by Herbert Schildt) - 适合快速入门。
- 在线教程和博客:
- Swing Tutorial on ZetCode - 提供了大量带有截图的实例教程。
- Mad Programmer's Blog - 由Swing专家编写,有很多高级技巧和自定义组件的示例。
希望这份指南能帮助你顺利入门Java Swing开发!祝你编程愉快!
