Java Swing 第一个程序
最后修改日期:2024 年 7 月 10 日
在本章中,我们将编写第一个 Swing 程序。 我们将创建一个简单的应用程序,展示如何使用“退出”按钮终止应用程序,显示框架图标,显示工具提示,使用助记符以及显示标准颜色。
Java Swing 组件是 Java Swing 应用程序的基本构建块。 在本章中,我们使用 JFrame
, JButton
和 JLabel
组件。 JFrame
是一个顶级窗口,带有标题和边框。 它用于组织其他组件,通常称为子组件。
JButton
是一个用于执行操作的按钮。 JLabel
是一个用于显示短文本字符串或图像,或两者兼有的组件。
Java Swing 第一个例子
第一个例子在屏幕上显示一个基本窗口。
package com.zetcode; import java.awt.EventQueue; import javax.swing.JFrame; public class SimpleEx extends JFrame { public SimpleEx() { initUI(); } private void initUI() { setTitle("Simple example"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new SimpleEx(); ex.setVisible(true); }); } }
虽然这段代码很短,但应用程序窗口可以做很多事情。 它可以被调整大小,最大化或最小化。 与之相关的所有复杂性都对应用程序程序员隐藏了。
import java.awt.EventQueue; import javax.swing.JFrame;
我们导入代码示例中使用的 Swing 类。
public class SimpleEx extends JFrame {
SimpleEx
继承自 JFrame
组件。 JFrame
是一个顶级容器。 容器的基本目的是容纳应用程序的组件。
public SimpleEx() { initUI(); }
将应用程序代码放入构造函数中,而是将任务委托给特定方法,这是一个良好的编程实践。
setTitle("Simple example");
在这里,我们使用 setTitle
方法设置窗口的标题。
setSize(400, 300);
setSize
将窗口大小调整为 400px 宽和 300px 高。
setLocationRelativeTo(null);
这行代码将窗口居中显示在屏幕上。
setDefaultCloseOperation(EXIT_ON_CLOSE);
这个 setDefaultCloseOperation
在点击标题栏的关闭按钮时关闭窗口。 默认情况下,如果单击该按钮,则什么都不会发生。
EventQueue.invokeLater(() -> { var ex = new SimpleEx(); ex.setVisible(true); });
我们创建代码示例的一个实例,并在屏幕上使其可见。 invokeLater
方法将应用程序放在 Swing 事件队列上。 它用于确保所有 UI 更新都是线程安全的。 换句话说,它用于防止 GUI 在某些情况下挂起。

JButton 示例
在下一个示例中,我们有一个按钮。 当我们点击按钮时,应用程序将终止。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import java.awt.EventQueue; public class QuitButtonEx extends JFrame { public QuitButtonEx() { initUI(); } private void initUI() { var quitButton = new JButton("Quit"); quitButton.addActionListener(_ -> System.exit(0)); createLayout(quitButton); setTitle("Quit button"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) ); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new QuitButtonEx(); ex.setVisible(true); }); } }
我们在窗口上放置一个 JButton
并在该按钮上添加一个动作监听器。
var quitButton = new JButton("Quit");
在这里,我们创建一个按钮组件。 此构造函数接受一个字符串标签作为参数。
quitButton.addActionListener(_ -> System.exit(0));
我们将一个动作监听器插入到该按钮中。 动作通过调用 System.exit
方法来终止应用程序。 我们使用在 Java 21 中预览并在 Java 22 中最终确定的未命名变量 _
。
createLayout(quitButton);
子组件需要放置到容器中。 我们将任务委托给 createLayout
方法。
var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl);
JFrame
的内容面板是放置子组件的区域。 子组件由称为布局管理器的特殊不可见组件组织。 内容面板的默认布局管理器是 BorderLayout
管理器。 此管理器非常简单。 在本教程中,我们使用功能更强大且更灵活的 GroupLayout
管理器。
gl.setAutoCreateContainerGaps(true);
setAutoCreateContainerGaps
方法在组件和容器的边缘之间创建间隙。 间隙或空间是每个应用程序设计的重要组成部分。
gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) );
GroupLayout
管理器独立地为每个维度定义布局。 在一个步骤中,我们在水平轴上排列组件; 在另一步骤中,我们在垂直轴上排列组件。
在这两种布局中,我们都可以按顺序或并行排列组件。 在水平布局中,一排组件称为顺序组,一列组件称为并行组。 在垂直布局中,一列组件称为顺序组,一排组件称为并行组。
在我们的示例中,我们只有一个按钮,因此布局非常简单。 对于每个维度,我们使用按钮组件作为参数调用 addComponent
方法。 (必须为两个维度添加每个子组件。)

JFrame 图标
在下面的示例中,我们将在框架上显示一个图标。 它显示在标题栏的左侧。
package com.zetcode; import java.awt.EventQueue; import javax.swing.ImageIcon; import javax.swing.JFrame; public class FrameIconEx extends JFrame { public FrameIconEx() { initUI(); } private void initUI() { var webIcon = new ImageIcon("src/resources/web.png"); setIconImage(webIcon.getImage()); setTitle("Icon"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new FrameIconEx(); ex.setVisible(true); }); } }
ImageIcon
用于创建图标。 web.png
是一个小的,22x22 像素的图像文件。
var webIcon = new ImageIcon("src/resources/web.png");
我们从一个 PNG 文件创建一个 ImageIcon
,该文件位于项目根目录中。
setIconImage(webIcon.getImage());
setIconImage
设置要显示为该窗口图标的图像。 getImage
返回图标的 Image
。

Java Swing 工具提示
工具提示是内部应用程序帮助系统的一部分。 如果我们将鼠标指针悬停在具有工具提示的某个对象上,Swing 将显示一个小窗口。
package com.zetcode; import java.awt.EventQueue; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JPanel; public class TooltipEx extends JFrame { public TooltipEx() { initUI(); } private void initUI() { var btn = new JButton("Button"); btn.setToolTipText("A button component"); createLayout(btn); setTitle("Tooltip"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = (JPanel) getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); pane.setToolTipText("Content pane"); gl.setAutoCreateContainerGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(200) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(120) ); pack(); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new TooltipEx(); ex.setVisible(true); }); } }
在该示例中,我们为框架和按钮设置工具提示。
btn.setToolTipText("A button component");
要启用工具提示,我们调用 setTooltipText
方法。
var pane = (JPanel) getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl);
内容面板是 JPanel
组件的一个实例。 getContentPane
方法返回一个 Container
类型。 由于设置工具提示需要 JComponent
实例,因此我们将对象转换为 JPanel
。
pane.setToolTipText("Content pane");
为内容面板设置工具提示。
gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(200) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(120) );
我们为水平和垂直维度调用 addGap
方法。 它在按钮的右侧和底部创建一些空间。(目的是增加窗口的初始大小。)
pack();
pack
方法根据其组件的大小自动调整 JFrame
的大小。 它也考虑了定义的空间。 我们的窗口显示按钮和我们使用 addGap
方法设置的空间。

Swing 助记符
助记符是激活支持助记符的组件的快捷键。 例如,它们可以与标签、按钮或菜单项一起使用。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; import java.awt.EventQueue; import java.awt.event.KeyEvent; public class MnemonicEx extends JFrame { public MnemonicEx() { initUI(); } private void initUI() { var btn = new JButton("Button"); btn.addActionListener(_ -> System.out.println("Button pressed")); btn.setMnemonic(KeyEvent.VK_B); createLayout(btn); setTitle("Mnemonics"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setHorizontalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(200) ); gl.setVerticalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addGap(200) ); pack(); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new MnemonicEx(); ex.setVisible(true); }); } }
我们有一个带有动作监听器的按钮。 我们为该按钮设置助记符。 它可以与 Alt+B 键盘快捷键一起激活。
btn.setMnemonic(KeyEvent.VK_B);
setMnemonic
方法设置按钮的键盘助记符。 助记符键使用 KeyEvent
类的虚拟键码指定。 助记符与外观和感觉的无鼠标修饰符(通常为 Alt)结合使用。
目前,有三种激活按钮的方法:鼠标左键单击、Alt+B 快捷键和 Space 键(前提是按钮具有焦点)。 Space 键绑定由 Swing 自动创建。(在 Metal 外观和感觉下,焦点在视觉上由按钮标签周围的小矩形表示。)
Java Swing 标准颜色
Color
类定义了十三种颜色值,包括红色、绿色、蓝色和黄色。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.util.ArrayList; class MyLabel extends JLabel { public MyLabel() { super("", null, LEADING); } @Override public boolean isOpaque() { return true; } } public class StandardColoursEx extends JFrame { public StandardColoursEx() { initUI(); } private void initUI() { Color[] stdCols = { Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, Color.yellow }; var labels = new ArrayList<JLabel>(); for (var col : stdCols) { var lbl = createColouredLabel(col); labels.add(lbl); } createLayout(labels.toArray(new JLabel[0])); setTitle("Standard colours"); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private JLabel createColouredLabel(Color col) { var lbl = new MyLabel(); lbl.setMinimumSize(new Dimension(90, 40)); lbl.setBackground(col); return lbl; } private void createLayout(JLabel[] labels) { var pane = (JPanel) getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); pane.setToolTipText("Content pane"); gl.setAutoCreateContainerGaps(true); gl.setAutoCreateGaps(true); gl.setHorizontalGroup(gl.createParallelGroup() .addGroup(gl.createSequentialGroup() .addComponent(labels[0]) .addComponent(labels[1]) .addComponent(labels[2]) .addComponent(labels[3])) .addGroup(gl.createSequentialGroup() .addComponent(labels[4]) .addComponent(labels[5]) .addComponent(labels[6]) .addComponent(labels[7])) .addGroup(gl.createSequentialGroup() .addComponent(labels[8]) .addComponent(labels[9]) .addComponent(labels[10]) .addComponent(labels[11])) .addComponent(labels[12]) ); gl.setVerticalGroup(gl.createSequentialGroup() .addGroup(gl.createParallelGroup() .addComponent(labels[0]) .addComponent(labels[1]) .addComponent(labels[2]) .addComponent(labels[3])) .addGroup(gl.createParallelGroup() .addComponent(labels[4]) .addComponent(labels[5]) .addComponent(labels[6]) .addComponent(labels[7])) .addGroup(gl.createParallelGroup() .addComponent(labels[8]) .addComponent(labels[9]) .addComponent(labels[10]) .addComponent(labels[11])) .addComponent(labels[12]) ); pack(); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new StandardColoursEx(); ex.setVisible(true); }); } }
该示例显示了十三个 JLabel
组件; 每个标签都有不同的背景颜色。 JLabel
通常用于显示文本; 但它也可以显示颜色。
class MyLabel extends JLabel { public MyLabel() { super("", null, LEADING); } @Override public boolean isOpaque() { return true; } }
JLabel
组件是一个特定组件,具有默认的透明背景。 为了在标签上绘画,我们覆盖 isOpaque
方法,该方法返回 true
。
Color[] stdCols = { Color.black, Color.blue, Color.cyan, Color.darkGray, Color.gray, Color.green, Color.lightGray, Color.magenta, Color.orange, Color.pink, Color.red, Color.white, Color.yellow };
在这里我们有一个内置的静态颜色值数组。
var labels = new ArrayList<JLabel>(); for (var col : stdCols) { var lbl = createColouredLabel(col); labels.add(lbl); }
创建 JLabel
组件列表。 使用 createColouredLabel
方法创建一个新标签。
public JLabel createColouredLabel(Color col) { var lbl = new MyLabel(); lbl.setMinimumSize(new Dimension(90, 40)); lbl.setBackground(col); return lbl; }
createColouredLabel
方法创建一个新标签。 我们为标签设置最小大小。 setBackground
设置组件的背景颜色。

Java Swing 鼠标移动事件
MouseMotionAdapter
用于接收鼠标移动事件。
package com.zetcode; import javax.swing.GroupLayout; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import java.awt.EventQueue; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; public class MouseMoveEventEx extends JFrame { private JLabel coords; public MouseMoveEventEx() { initUI(); } private void initUI() { coords = new JLabel(""); createLayout(coords); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { super.mouseMoved(e); int x = e.getX(); int y = e.getY(); var text = String.format("x: %d, y: %d", x, y); coords.setText(text); } }); setTitle("Mouse move events"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } private void createLayout(JComponent... arg) { var pane = getContentPane(); var gl = new GroupLayout(pane); pane.setLayout(gl); gl.setAutoCreateContainerGaps(true); gl.setHorizontalGroup(gl.createParallelGroup() .addComponent(arg[0]) .addGap(250) ); gl.setVerticalGroup(gl.createSequentialGroup() .addComponent(arg[0]) .addGap(130) ); pack(); } public static void main(String[] args) { EventQueue.invokeLater(() -> { var ex = new MouseMoveEventEx(); ex.setVisible(true); }); } }
在该示例中,我们在标签组件中显示鼠标指针的坐标。
addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { super.mouseMoved(e); int x = e.getX(); int y = e.getY(); var text = String.format("x: %d, y: %d", x, y); coords.setText(text); } });
我们覆盖了适配器的 moseMoved
方法。 从 MouseEvent
对象中,我们获取鼠标指针的 x 和 y 坐标,构建一个字符串,并将其设置为标签。

在本章中,我们创建了一些简单的 Java Swing 程序。