文章目录
历史回顾:曾经的Java桌面应用
0. 前言
Java 桌面应用 一般由 Java Swing 提供。是现在已经淘汰的技术,工作中已经几乎不用了,近几年出的新版的JDK 也已不再支持 Java Swing。那我们为什么还要学习它呢?
其实 Java Swing 也是很帅的,如果您跟我一样没有前端基础,那么单凭Java Swing 就可以用纯 Java 做一些小玩意,这不,最近我的领导就用 GPT + Copilot + Java Swing 半小时就模拟出了一个QQ,帅的咧。
为什么要学习 Java Swing
- 回顾历史,或许能对现在有新的启发
- 对于学生考试而言,现在很多大学的Java课程依然涉及Java Swing,帮助考试
- 学完可以自己做点小型桌面应用
- 实在想不到写点啥,水一篇文章
参考教程
how2j的Java教程:https://how2j.cn/
如果上述学习的理由与您的情况或您的想法不匹配,那么您可以把这篇文章划走了,有一说一,实际工作真不怎么会用到 Java Swing 了。
1. Java Swing
学习 Swing 这个GUI 技术有个小前提,就是首先要了解:Swing 的窗口是基于 像素和坐标的,下文出现的Location,指的都是像素和坐标。关于GUI 的像素和坐标,读者可事先参考其他资料。
1.1 Hello Swing
使用 Java Swing 其实就是使用 javax.swing 包,直接用包里的类和方法即可,只需要知道什么方法是干什么的就行,具体常见方法我会在下面介绍,学习成本低。
我们首先来个 Hello Swing 吧:
public static void main(String[] args) {
// 主窗口
JFrame f = new JFrame("Hello Swing");
// 设置窗口大小
f.setSize(400, 300);
// 主窗口设置位置
f.setLocation(200, 200);
// 设置为绝对定位
f.setLayout(null);
// 按钮组件
JButton b = new JButton("Hello");
// 设置按钮大小和位置
b.setBounds(50, 50, 280, 30);
// 添加按钮到主窗口
f.add(b);
// 设置主窗口关闭时的操作
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 显示主窗口
f.setVisible(true);
}
运行结果:
1.2 Swing 监听器
Swing 的监听器 可以让我们 对 Swing写出来的GUI界面操作 的 时候, 通过监听器来接收我们的操作信息,并执行相应的操作。
Swing 的监听器分为按钮监听、键盘监听、鼠标监听、适配器等分类。都是为GUI交互而设计的。
1.2.1 按钮监听
通过 button 按钮 绑定 ActionListener 接口的匿名内部类。Swing的代码都很好阅读,我们举个例子:
public static void main(String[] args) {
// main window
JFrame f = new JFrame("Hello listener");
f.setSize(400, 300);
f.setLocation(200, 200);
f.setLayout(null);
// label
JLabel l = new JLabel();
// image for test
ImageIcon icon = new ImageIcon("E:\\Study\\ResourceAndNotes\\Java\\PersonalNotes\\images\\reflect.png");
// set image
l.setIcon(icon);
// set label size and location
l.setBounds(50, 50, icon.getIconWidth(), icon.getIconHeight());
// button
JButton b = new JButton("Click me disappear icon");
b.setBounds(50, 200, 280, 30);
// add button listener
b.addActionListener(e -> l.setVisible(false));
f.add(l);
f.add(b);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
实现的效果是,点击按钮,图片消失。
1.2.2 键盘监听
使用 KeyListener 实现键盘监听, KeyListener 的方法都是见名知意的,非常好理解:
public static void main(String[] args) {
// window
javax.swing.JFrame f = new javax.swing.JFrame("Hello listener");
f.setSize(400, 300);
f.setLocation(200, 200);
f.setLayout(null);
// label
javax.swing.JLabel l = new javax.swing.JLabel();
// image for test
javax.swing.ImageIcon icon = new javax.swing.ImageIcon("E:\\Study\\ResourceAndNotes\\Java\\PersonalNotes\\images\\reflect.png");
// set image
l.setIcon(icon);
// set label size and location
l.setBounds(50, 50, icon.getIconWidth(), icon.getIconHeight());
// add KeyListener
f.addKeyListener(new java.awt.event.KeyListener() {
@Override
public void keyTyped(KeyEvent keyEvent) {
if (keyEvent.getKeyChar() == 'a') {
l.setLocation(l.getX() - 10, l.getY());
}
if (keyEvent.getKeyChar() == 'd') {
l.setLocation(l.getX() + 10, l.getY());
}
if (keyEvent.getKeyChar() == 'w') {
l.setLocation(l.getX(), l.getY() - 10);
}
if (keyEvent.getKeyChar() == 's') {
l.setLocation(l.getX(), l.getY() + 10);
}
}
// key pressed
@Override
public void keyPressed(KeyEvent keyEvent) {
}
// key released
@Override
public void keyReleased(KeyEvent keyEvent) {
}
});
f.add(l);
f.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
1.2.3 鼠标监听
MouseListener , 也提供了鼠标按下、释放、退出、进入、点击等事件的监听:
public static void main(String[] args) {
// window
javax.swing.JFrame f = new javax.swing.JFrame("Hello listener");
f.setSize(400, 300);
f.setLocation(200, 200);
f.setLayout(null);
// label
javax.swing.JLabel l = new javax.swing.JLabel();
// image for test
javax.swing.ImageIcon icon = new javax.swing.ImageIcon("E:\\Study\\ResourceAndNotes\\Java\\PersonalNotes\\images\\reflect.png");
// set image
l.setIcon(icon);
// set label size and location
l.setBounds(50, 50, icon.getIconWidth(), icon.getIconHeight());
// add MouseListener
l.addMouseListener(new java.awt.event.MouseListener() {
@Override
public void mouseClicked(java.awt.event.MouseEvent mouseEvent) {
System.out.println("mouse clicked");
}
@Override
public void mousePressed(java.awt.event.MouseEvent mouseEvent) {
System.out.println("mouse pressed");
}
@Override
public void mouseReleased(java.awt.event.MouseEvent mouseEvent) {
System.out.println("mouse released");
}
@Override
public void mouseEntered(java.awt.event.MouseEvent mouseEvent) {
// move image with mouse
l.setLocation(mouseEvent.getXOnScreen() - icon.getIconWidth() / 2, mouseEvent.getYOnScreen() - icon.getIconHeight() / 2);
}
@Override
public void mouseExited(java.awt.event.MouseEvent mouseEvent) {
System.out.println("mouse exited");
}
});
f.add(l);
f.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
1.3 Swing 容器
我们可以将 一个窗口看作一个容器,组件都放到容器里。
我们上文提到的 Jframe 就是一个容器:
public static void main(String[] args) {
// 主窗口
JFrame f = new JFrame("Hello Swing");
// 设置窗口大小
f.setSize(400, 300);
// 主窗口设置位置
f.setLocation(200, 200);
// 设置为绝对定位
f.setLayout(null);
// 按钮组件
JButton b = new JButton("Hello");
// 设置按钮大小和位置
b.setBounds(50, 50, 280, 30);
// 添加按钮到主窗口
f.add(b);
// 设置主窗口关闭时的操作
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 显示主窗口
f.setVisible(true);
}
除了Jframe, Swing 还提供了 JDialog 作为容器:
public static void main(String[] args) {
// using JDialog to create a window
javax.swing.JDialog d = new javax.swing.JDialog();
// set window title
d.setTitle("Hello JDialog");
// set window size
d.setSize(400, 300);
// set window location
d.setLocation(200, 200);
// set window layout
d.setLayout(null);
// set window visible
d.setVisible(true);
}
1.4 Swing 布局
Swing 的布局由布局器提供,布局器常见的布局有:
- FlowLayout 顺序布局
- BorderLayout 上下左右中间的顺序布局
- GirdLayout 网格布局器
- CardLayout 面板
举个例子,我们做个计算器:
public class Calculator extends JFrame implements ActionListener {
private JTextField displayField;
public Calculator() {
setTitle("Calculator");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
// 创建显示区域
displayField = new JTextField();
displayField.setEditable(false);
add(displayField, BorderLayout.NORTH);
// 创建按钮面板
JPanel buttonPanel = new JPanel(new GridLayout(4, 4));
String[] buttonLabels = {
"7", "8", "9", "/",
"4", "5", "6", "*",
"1", "2", "3", "-",
"0", ".", "=", "+"
};
for (String label : buttonLabels) {
JButton button = new JButton(label);
button.addActionListener(this);
buttonPanel.add(button);
}
add(buttonPanel, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null); // 居中显示
}
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
String currentText = displayField.getText();
if (command.equals("=")) {
// 计算结果
try {
double result = evaluateExpression(currentText);
displayField.setText(Double.toString(result));
} catch (Exception ex) {
displayField.setText("Error");
}
} else {
// 在当前文本后追加按钮上的标签
displayField.setText(currentText + command);
}
}
private double evaluateExpression(String expression) {
// 使用适当的算法计算表达式的结果
// 这里仅为示例,可以使用自己的实现或相关库来计算表达式
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < expression.length()) ? expression.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < expression.length()) throw new RuntimeException("Unexpected: " + (char) ch);
return x;
}
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // 加法
else if (eat('-')) x -= parseTerm(); // 减法
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // 乘法
else if (eat('/')) x /= parseFactor(); // 除法
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // 正号
if (eat('-')) return -parseFactor(); // 负号
double x;
int startPos = this.pos;
if (eat('(')) { // 括号
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // 数字
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(expression.substring(startPos, this.pos));
} else {
throw new RuntimeException("Unexpected: " + (char) ch);
}
return x;
}
}.parse();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Calculator calculator = new Calculator();
calculator.setVisible(true);
});
}
}
这个计算器使用了BorderLayout布局管理器来实现
1.5 Swing 组件
Swing 提供了丰富的 GUI 组件,包括按钮、标签、文本框、复选框、单选框、下拉框、列表、表格等等。通过使用这些组件,我们可以构建出功能丰富的用户界面。
例如,下面是一个使用表格组件的示例代码,展示如何创建一个简单的表格并填充数据:
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class TableExample extends JFrame {
private JTable table;
public TableExample() {
setTitle("Table Example");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 创建表格模型和表格
DefaultTableModel model = new DefaultTableModel();
model.addColumn("Name");
model.addColumn("Age");
model.addRow(new Object[]{"Alice", 25});
model.addRow(new Object[]{"Bob", 30});
table = new JTable(model);
// 将表格放置在滚动面板中
JScrollPane scrollPane = new JScrollPane(table);
// 将滚动面板添加到窗口中
getContentPane().add(scrollPane);
pack();
setLocationRelativeTo(null); // 居中显示
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TableExample frame = new TableExample();
frame.setVisible(true);
});
}
}
2. 总结与补充
以上介绍了 Java Swing 的基本知识点,包括创建窗口、使用布局管理器、使用组件、注册监听器等。由于Java Swing 已 被淘汰,且调用 Swing 也仅仅是直接拿来用,并不涉及更多的开发思想,目前这部分技术已成历史,成为了我们选学的部分。当然,当今依然有很多高校在教Java Swing,其目的更多地是利用这个曾经占有一定市场份额的GUI技术带同学们入门,以及应付考试。
如果你对 Java Swing 很感兴趣,想单纯地用 Swing 实现自己的桌面应用,那么可以自行查阅支持 Swing 的 JDK API 文档。