背景

首先本文是一个系列的文章,起名为《Java Swing GUi从小白到大神》,主要介绍Java Swing相关概念和实践应用。目前JavaSwingGUI很少作为界面的开发框架,但是对于学习Java体系,以及新手将来学习后端之后,能快速从前后端整体去理解一套系统是非常有帮助的,所以是很有必要学习和掌握的。本篇是系列博文的第二篇,若想详细学习请点击首篇博文开始,让我们开始吧。

文章概览

  1. 如何使用面板组件
  2. Swing事件处理机制
  3. 如何使用列表框和下拉列表框组件
  4. 如何使用进度条、时间、滑块和分隔条组件

4. 如何使用面板组件

4.1 如何使用JPanel
JPanel是在实际项目使用最频率最高的面板之一,其默认布局为FlowLayout,且为双缓冲技术,这里再解释一下双缓冲技术,既是为了提升性能,也是为了解决组件重绘时的闪烁问题。代码示例如下:

代码使用JPanelExample
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class JpanelExample {
    static final int WIDTH = 300;
    static final int HEIGHT = 150;

    public static void main(String[] args) {
        JFrame frame = new JFrame("JPanel测试程序");
        frame.setSize(WIDTH, HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JPanel panel = new JPanel();
        panel.setLayout(new java.awt.BorderLayout());
        frame.setContentPane(panel);

        JPanel panel1 = new JPanel(new java.awt.FlowLayout());
        JPanel panel2 = new JPanel(new java.awt.FlowLayout());
        JPanel panel3 = new JPanel(new java.awt.FlowLayout());
        JPanel panel4 = new JPanel(new java.awt.FlowLayout());
        JPanel panel5 = new JPanel(new java.awt.FlowLayout());

        JButton button1 = new JButton("小赵");
        JButton button2 = new JButton("小李");
        JButton button3 = new JButton("小敢");
        JButton button4 = new JButton("小武");
        JButton button5 = new JButton("姓");
        JButton button6 = new JButton("小钱");
        JButton button7 = new JButton("小周");
        JButton button8 = new JButton("小王");
        JButton button9 = new JButton("小孙");

        panel1.add(button1);
        panel1.add(button2);
        panel2.add(button3);
        panel2.add(button4);
        panel3.add(button5);
        panel3.add(button6);
        panel4.add(button7);
        panel4.add(button8);
        panel5.add(button9);

        panel.add(panel1, "North");
        panel.add(panel2, "South");
        panel.add(panel3, "East");
        panel.add(panel4, "West");
        panel.add(panel5, "Center");

        frame.setVisible(true);
    }
}

4.2 如何使用JScrollPane
JScrollPane是一个带滚动条的容器,它可以用来显示一些文本、表格等内容。示例代码如下:

代码使用JScrollPaneExample
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class JScrollPaneExample {
    static final int WIDTH = 300;
    static final int HEIGHT = 150;

    public static void main(String[] args) {
        JFrame frame = new JFrame("JScrollPane测试程序");
        frame.setSize(WIDTH, HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JTextArea textArea = new JTextArea("放大交流封疆大吏发冷风机阿拉基反垃圾发啦");
        JScrollPane scrollPane = new JScrollPane(textArea,
                JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

        frame.setContentPane(scrollPane);
        frame.setVisible(true);
    }
}

4.3 如何使用JSplitPane
JSplitPane面板主要是将不同组件分隔开来。

  1. setOneTouchExpandable(boolean):
  • 该方法用于设置 JSplitPane 的分隔条是否可以通过点击快速展开或收缩面板。
  • true:允许用户通过点击分隔条的上下/左右箭头来展开或收缩面板。
  • false:禁用这个功能,分隔条只能通过拖动来调整面板大小。 默认情况下,setOneTouchExpandable 是关闭的,用户只能通过拖动分隔条调整大小。
  1. setContinuousLayout(boolean):
  • 该方法用于设置分隔条调整时,布局是否实时更新。
  • true:启用连续布局,当分隔条位置变化时,面板会立即更新布局。适用于需要快速响应的界面。
  • false:禁用连续布局,调整分隔条时,布局的更新会延迟,可能会导致界面更平滑,但更新不够即时。 默认情况下,setContinuousLayout 是启用的,这意味着分隔条拖动时面板会立刻响应并更新
  1. setOrientation(int):
  • 该方法用于设置 JSplitPane 的分割方向。
  • JSplitPane.HORIZONTAL_SPLIT:水平分割,两个面板左右排列。
  • JSplitPane.VERTICAL_SPLIT:垂直分割,两个面板上下排列。 默认情况下,JSplitPane 是水平分割。你可以通过此方法设置垂直分割来改变面板的排列方式。
  1. setDividerLocation(int):
  • 该方法用于设置分隔条的位置。单位是像素。传入的值表示分隔条的初始位置,决定了面板的初始大小比例。
  • 比如,setDividerLocation(200) 表示将分隔条的位置设置为 200 像素。 默认情况下,分隔条的位置会根据容器的大小和组件的尺寸自动决定,但你可以手动设置它。
代码使用JSplitPaneExample
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
public class JSplitPaneExample {
    public static void main(String[] args) {
        // 创建主窗体
        JFrame frame = new JFrame("JSplitPane 示例");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);

        // 创建两个JPanel面板 p1 和 p2
        JPanel p1 = new JPanel();
        JPanel p2 = new JPanel();

        // 设置面板背景色
        p1.setBackground(Color.CYAN);
        p2.setBackground(Color.LIGHT_GRAY);

        // 在 p1 面板中添加两个按钮
        JButton button1 = new JButton("按钮 1");
        JButton button2 = new JButton("按钮 2");

        // 将按钮添加到 p1 面板
        p1.setLayout(new FlowLayout()); // 使用 FlowLayout 管理按钮
        p1.add(button1);
        p1.add(button2);

        // 在 p2 面板中添加两个按钮
        JButton button3 = new JButton("按钮 3");
        JButton button4 = new JButton("按钮 4");

        // 将按钮添加到 p2 面板
        p2.setLayout(new FlowLayout()); // 使用 FlowLayout 管理按钮
        p2.add(button3);
        p2.add(button4);

        // 创建 JSplitPane,设置水平分割
        JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, p1, p2);

        // 设置分隔条的位置,0.5代表在中间
        splitPane.setDividerLocation(200);

        // 设置分隔条是否可以通过点击和拖动来进行调整,默认是可以的
        splitPane.setOneTouchExpandable(true); // 使得分隔条可以通过点击来展开或收缩
        // splitPane.setOneTouchExpandable(false); // 如果需要禁用点击展开/收缩功能,可以设置为 false

        // 设置布局是否连续更新。默认情况下,JSplitPane 会在用户调整分隔条时立即更新布局。
        splitPane.setContinuousLayout(true); // 启用连续布局,调整分隔条时,面板会立即重新布局
        // splitPane.setContinuousLayout(false); // 禁用连续布局,可以减少频繁的界面更新

        // 设置 JSplitPane 的分割方向,水平分割还是垂直分割
        // JSplitPane.HORIZONTAL_SPLIT:水平分割
        // JSplitPane.VERTICAL_SPLIT:垂直分割
        splitPane.setOrientation(JSplitPane.HORIZONTAL_SPLIT); // 默认是水平分割
        // splitPane.setOrientation(JSplitPane.VERTICAL_SPLIT); // 如果需要垂直分割,可以改为垂直分割

        // 设置分隔条的位置。单位是像素,可以通过改变此位置来调整初始分隔条位置
        splitPane.setDividerLocation(200); // 设置分隔条在 200 像素的位置

        // 将 JSplitPane 添加到主窗体
        frame.add(splitPane);
        frame.setVisible(true);
    }
}

4.4 如何使用JTabbedPane
JTabbedPane面板主要用来创建选项卡容器,代码示例如下:

代码使用JTabbedPaneExample
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class JTabbedPaneExample {
    public static void main(String[] args) {
        // 创建一个新的窗口框架并设置标题
        JFrame frame = new JFrame("JTabbedPane测试程序");
        // 设置窗口的大小
        frame.setSize(400, 300);
        // 设置窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 创建多个面板,用于放置不同的收入情况标签
        JPanel panel1 = new JPanel(new java.awt.FlowLayout());
        JPanel panel2 = new JPanel(new java.awt.FlowLayout());
        JPanel panel3 = new JPanel(new java.awt.FlowLayout());
        JPanel panel4 = new JPanel(new java.awt.FlowLayout());
        JPanel panel5 = new JPanel(new java.awt.FlowLayout());

        // 创建多个标签,用于展示个人收入情况的详细信息
        JLabel label1 = new JLabel("个人收入情况");
        JLabel label2 = new JLabel("工资情况 3000/月");
        JLabel label3 = new JLabel("奖金情况 500/月");
        JLabel label4 = new JLabel("津贴情况 1000/月");
        JLabel label5 = new JLabel("社保情况 1000/月");

        // 将标签添加到对应的面板中
        panel1.add(label1);
        panel2.add(label2);
        panel3.add(label3);
        panel4.add(label4);
        panel5.add(label5);

        // 创建一个标签页组件
        JTabbedPane tabbedPane = new JTabbedPane();
        // 添加多个标签页,每个标签页展示一个面板的内容
        tabbedPane.addTab("个人收入情况", panel1);
        tabbedPane.setEnabledAt(0, true);
        tabbedPane.addTab("工资", panel2);
        tabbedPane.setEnabledAt(1, true);
        tabbedPane.addTab("奖金", panel3);
        tabbedPane.setEnabledAt(2, true);
        tabbedPane.addTab("津贴", panel4);
        tabbedPane.setEnabledAt(3, true);
        tabbedPane.addTab("社保", panel5);
        tabbedPane.setEnabledAt(4, true);

        // 设置标签页组件的首选尺寸和标签位置,以及标签布局策略
        tabbedPane.setPreferredSize(new Dimension(500, 200));
        tabbedPane.setTabPlacement(JTabbedPane.TOP);
        tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);

        // 将标签页组件添加到窗口框架中
        frame.add(tabbedPane);
        // 显示窗口
        frame.setVisible(true);
    }
}

4.5 如何使用JInternalFrame
JInternalFrame面板用法与JPanel用法类似,唯一不同JInternalFrame可以嵌套在JDesktopPane中,并且可以设置是否可以关闭、是否可以最小化等属性。代码示例如下:

  1. JDesktopPane:
  • JDesktopPane 是用于管理多个 JInternalFrame 的容器。在主窗体 (JFrame) 中创建一个 JDesktopPane,并将它添加到窗体中。
  1. JInternalFrame:
  • 每个 JInternalFrame 是一个子窗口,可以包含各种组件。我们在创建 JInternalFrame 时设置了它的标题、是否可关闭、是否可最大化、是否可调大小等属性。
  • 通过 JInternalFrame 的构造器,设置它的初始状态:标题(title)、是否可以调整大小、是否可以最大化和最小化
代码使用JInternalFrameExample
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class JInternalFrameExample {
    public static void main(String[] args) {
        // 创建主窗体
        JFrame frame = new JFrame("JInternalFrame 示例");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 600);

        // 创建桌面面板 JDesktopPane,不设置布局管理器
        JDesktopPane desktop = new JDesktopPane();

        // 创建两个JInternalFrame窗口
        JInternalFrame internalFrame1 = createInternalFrame("窗口 1", "这是第一个内部窗口。");
        JInternalFrame internalFrame2 = createInternalFrame("窗口 2", "这是第二个内部窗口。");

        // 设置每个内部窗口的位置,避免重叠
        internalFrame1.setLocation(50, 50); // 第一个窗口位置
        internalFrame2.setLocation(400, 50); // 第二个窗口位置,稍微偏移

        // 将内部窗口添加到桌面面板
        desktop.add(internalFrame1);
        desktop.add(internalFrame2);

        // 将桌面面板添加到主窗体
        frame.add(desktop);
        frame.setVisible(true);

        // 显示内部窗口
        internalFrame1.setVisible(true);
        internalFrame2.setVisible(true);
    }

    // 创建一个内部窗口,并在其中添加一个 JLabel,使用流式布局
    public static JInternalFrame createInternalFrame(String title, String labelText) {
        // 创建内部窗口,设置窗口标题、大小和是否可关闭
        JInternalFrame internalFrame = new JInternalFrame(title, true, true, true, true);
        internalFrame.setSize(300, 200);

        // 设置内部窗口的布局管理器为 FlowLayout
        internalFrame.setLayout(new FlowLayout());

        // 创建 JLabel 并添加文本
        JLabel label = new JLabel(labelText);
        label.setFont(new Font("Serif", Font.PLAIN, 16)); // 设置字体

        // 将 JLabel 添加到内部窗口
        internalFrame.add(label);

        // 设置内部窗口的可见性和布局
        internalFrame.setDefaultCloseOperation(JInternalFrame.DISPOSE_ON_CLOSE);

        return internalFrame;
    }
}

4.6 如何使用JLayeredPane
JLayeredPane 允许你在不同的层次上放置组件,每个组件都有一个“层级”,组件在更高层级时会显示在较低层级的组件之上。使用 setLayer(Component c, int layer) 来设置按钮的层级。代码示例如下:

代码使用JLayeredPane
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class JLayeredPaneExample {
    public static void main(String[] args) {
        // 创建主窗口
        JFrame frame = new JFrame("JLayeredPane 按钮切换层级");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);

        // 创建JLayeredPane组件
        JLayeredPane layeredPane = new JLayeredPane();
        frame.add(layeredPane);

        // 创建两个按钮
        JButton button1 = new JButton("按钮1");
        JButton button2 = new JButton("按钮2");

        // 设置按钮初始位置
        button1.setBounds(50, 50, 100, 40); // 第一个按钮位置
        button2.setBounds(80, 80, 100, 40); // 第二个按钮位置,部分重叠

        // 添加按钮到JLayeredPane
        layeredPane.add(button1, Integer.valueOf(200));
        layeredPane.add(button2, Integer.valueOf(300));

        // 为按钮1添加点击事件,交换按钮的层级
        button1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals("按钮1")) {
                    // 交换两个按钮的层级
                    layeredPane.setLayer(button1, 300);
                    layeredPane.setLayer(button2, 200);
                }
            }
        });

        // 为按钮2添加点击事件,交换按钮的层级
        button2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals("按钮2")) {
                    // 交换两个按钮的层级
                    layeredPane.setLayer(button1, 200);
                    layeredPane.setLayer(button2, 300);
                }
            }
        });

        // 显示窗口
        frame.setVisible(true);
    }
}

4.7 如何使用JRootPane

graph TB
    P[JFrame] --> A[JRootPane]
    A[JRootPane] --> B[GlassPane]
    A[JRootPane] --> C[JLayeredPane]
    C[JLayeredPane] --> D[ContentPane]
    C[JLayeredPane] --> E[JMenuBar]

JRootPane 是 Java Swing 中的一个重要组件,它作为顶级容器,用于在 JFrame 和其他容器中管理窗口的布局和行为。JRootPane 主要作用是提供一个基础结构,管理窗口的内容面板、装饰边框、菜单栏等。在大多数情况下,我们并不直接使用 JRootPane,而是通过 JFrame 或其他容器来间接使用它,比如JFrame 提供了更高层次的接口(如 setContentPane 和 setJMenuBar),当进行操作时底层其实在调用JRootPane,因为它是 JFrame 内部使用的主要面板。代码示例如下:

代码JRootPaneExample
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public class JRootPaneExample {
    public static void main(String[] args) {
        // 创建 JFrame
        JFrame frame = new JFrame("JRootPane 示例");

        // 创建一个JRootPane实例
        JRootPane rootPane = frame.getRootPane();

        // 设置JMenuBar(菜单栏)
        JMenuBar menuBar = new JMenuBar();
        JMenu fileMenu = new JMenu("文件");
        JMenuItem openItem = new JMenuItem("打开");
        JMenuItem exitItem = new JMenuItem("退出");
        fileMenu.add(openItem);
        fileMenu.add(exitItem);
        menuBar.add(fileMenu);
        frame.setJMenuBar(menuBar); // 设置菜单栏

        // 设置内容面板
        JPanel contentPanel = new JPanel();
        contentPanel.setLayout(new BorderLayout());
        contentPanel.add(new JLabel("这是内容面板", JLabel.CENTER), BorderLayout.CENTER);

        // 在内容面板中添加按钮
        JButton button1 = new JButton("按钮 1");
        JButton button2 = new JButton("按钮 2");
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(button1);
        buttonPanel.add(button2);
        contentPanel.add(buttonPanel, BorderLayout.SOUTH); // 按钮放在底部

        frame.setContentPane(contentPanel); // 设置内容面板

        // 设置分层面板
        JLayeredPane layeredPane = rootPane.getLayeredPane();
        layeredPane.setLayer(contentPanel, JLayeredPane.DEFAULT_LAYER);

        // 设置玻璃面板
        JPanel glassPanel = new JPanel();
        glassPanel.setBackground(new Color(0, 0, 0, 100)); // 半透明黑色
        glassPanel.setLayout(new BorderLayout());
        glassPanel.add(new JLabel("这是玻璃面板", JLabel.CENTER), BorderLayout.CENTER);
        frame.setGlassPane(glassPanel); // 设置玻璃面板
        glassPanel.setVisible(false); // 默认不显示, true则看不到菜单和按钮

        // 设置默认按钮(如果需要)
        JButton defaultButton = new JButton("默认按钮");
        frame.getRootPane().setDefaultButton(defaultButton);
        contentPanel.add(defaultButton, BorderLayout.NORTH); // 将默认按钮添加到北部(顶部)

        // 设置窗口关闭操作
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(400, 300);
        frame.setVisible(true);
    }
}

5. Swing事件处理机制

  • Swing事件处理机制概述
  • Swing中的监控器
    不同事件对应不同事件处理器,所谓事件源就是触发动作的按钮,下面示例图示了事件处理器和事件源的关系:
graph LR
    A[动作事件] --> D[事件]
    B[鼠标事件]  --> D[事件]
    C[焦点事件]  --> D[事件]
    D --> E[事件源]
    E --> F[事件监听者]
    F --> G[动作监听器]
    F --> H[鼠标监听器]
    F --> I[焦点监听器]

5.1 事件处理过程与步骤

  • 向事件源注入监听器
  • 创建监听器
  • 实现监听事件接口
代码清空文本域
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class ClearTextExample {

    public static void main(String[] args) {
        // 创建 JFrame 窗口
        JFrame frame = new JFrame("清空文本域");

        // 设置窗口关闭时的默认操作
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 创建一个文本域 JTextArea
        JTextArea textArea = new JTextArea(10, 30);
        textArea.setBorder(BorderFactory.createLineBorder(Color.BLACK)); // 设置边框
        textArea.setLineWrap(true); // 自动换行
        textArea.setMargin(new Insets(10, 10, 10, 10)); // 设置文本域的内边距(上下左右为10)

        // 创建一个按钮 JButton
        JButton clearButton = new JButton("清空文本");

        // 设置按钮的事件监听器且为匿名事件监听器
        clearButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 当按钮被点击时,清空文本域的内容
                textArea.setText("");
            }
        });

        // 创建一个面板 JPanel,并设置其布局为 BorderLayout
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout()); // 使用 BorderLayout 布局

        // 将文本域和按钮添加到面板
        panel.add(new JScrollPane(textArea), BorderLayout.CENTER); // 文本域放在中心
        panel.add(clearButton, BorderLayout.SOUTH); // 按钮放在底部

        // 设置面板的外边距(间隙)
        panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); // 上右下左都为20像素的间隙

        // 将面板设置为窗口的内容面板
        frame.setContentPane(panel);

        // 设置窗口的大小和显示
        frame.setSize(400, 300);
        frame.setVisible(true);
    }
}

5.2 适配器类
适配器类(Adapter Class)是一种通过继承现有接口并提供默认实现来简化事件监听器代码的机制。它是事件监听器的简化版本

  • ComponentAdapter 类: 是 ComponentListener 接口的适配器类,常用于监听组件的大小、位置、显示状态等变化事件。

    ComponentAdapter类源码
    1
    2
    3
    4
    5
    6
    
    public abstract class ComponentAdapter implements ComponentListener {
        public void componentResized(ComponentEvent e) {}
        public void componentMoved(ComponentEvent e) {}
        public void componentShown(ComponentEvent e) {}
        public void componentHidden(ComponentEvent e) {}
    }
    

  • 使用场景: 当你只关心 componentResized() 方法时,可以继承 ComponentAdapter 并只重写该方法。

    componentResized()方法
    1
    2
    3
    4
    5
    6
    7
    
    JButton button = new JButton("Resize Me");
    button.addComponentListener(new ComponentAdapter() {
        @Override
        public void componentResized(ComponentEvent e) {
            System.out.println("Button resized!");
        }
    });
    

  • ContainerAdapter 类: 是 ContainerListener 接口的适配器类,通常用于监听容器(如 JPanel)中的组件添加和删除事件。

    ContainerAdapter类源码
    1
    2
    3
    4
    
    public abstract class ContainerAdapter implements ContainerListener {
        public void componentAdded(ContainerEvent e) {}
        public void componentRemoved(ContainerEvent e) {}
    }
    

  • 使用场景:
    当你只关心 componentAdded() 方法时,可以继承 ContainerAdapter 并只重写该方法。

    componentAdded()方法
    1
    2
    3
    4
    5
    6
    7
    
    JPanel panel = new JPanel();
    panel.addContainerListener(new ContainerAdapter() {
        @Override
        public void componentAdded(ContainerEvent e) {
            System.out.println("Component added to the panel!");
        }
    });
    

  • FocusAdapter类: 是 FocusListener 接口的适配器类,用于监听组件的焦点变化事件(如获取焦点和失去焦点)。

    FocusAdapter类源码
    1
    2
    3
    4
    
    public abstract class FocusAdapter implements FocusListener {
        public void focusGained(FocusEvent e) {}
        public void focusLost(FocusEvent e) {}
    }
    

  • 使用场景:
    当你只关心 focusGained() 方法时,可以继承 FocusAdapter 并只重写该方法。

    focusGained()方法
    1
    2
    3
    4
    5
    6
    7
    
    JTextField textField = new JTextField("Click to focus");
    textField.addFocusListener(new FocusAdapter() {
        @Override
        public void focusGained(FocusEvent e) {
            System.out.println("TextField gained focus!");
        }
    });
    

  • KeyAdapter类: 是 KeyListener 接口的适配器类,它为 KeyListener 接口中的所有方法提供了默认的实现。你可以根据需要只重写其中一个方法,而不必实现接口中的所有方法。

    KeyAdapter类源码
    1
    2
    3
    4
    5
    
    public abstract class KeyAdapter implements KeyListener {
        public void keyTyped(KeyEvent e) {}
        public void keyPressed(KeyEvent e) {}
        public void keyReleased(KeyEvent e) {}
    }
    

  • 使用场景:
    KeyAdapter 主要用于当你只关心某些键盘事件时。例如,你可能只关心键盘的按下(keyPressed())或释放(keyReleased())事件,而不关心输入字符(keyTyped())事件。

    keyPressed()方法
    1
    2
    3
    4
    5
    
    textField.addKeyListener(new KeyAdapter() {
        @Override
        public void keyPressed(KeyEvent e) {
            System.out.println("Key Pressed: " + e.getKeyChar());  // 按下键时,输出按下的字符
        }
    

  • MouseAdapter 类: 是 MouseListener 接口的适配器类,它为 MouseListener 接口的所有方法提供了默认的空实现。

    MouseAdapter类源码
    1
    2
    3
    4
    5
    6
    7
    
    public abstract class MouseAdapter implements MouseListener {
        public void mouseClicked(MouseEvent e) {}
        public void mousePressed(MouseEvent e) {}
        public void mouseReleased(MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}
        public void mouseExited(MouseEvent e) {}
    }
    

  • 使用场景:
    MouseAdapter 主要用于监听鼠标事件,尤其是当你只关心其中的某些事件时。例如,你只需要处理鼠标点击事件,但并不关心鼠标按下或释放等事件。

    mouseClicked()方法
    1
    2
    3
    4
    5
    6
    7
    
    // 使用 MouseAdapter 监听鼠标点击事件
    button.addMouseListener(new MouseAdapter() {
        @Override
        public void mouseClicked(MouseEvent e) {
            System.out.println("Button clicked!");
        }
    });
    

  • MouseMotionAdapter类: 是 MouseMotionListener 接口的适配器类,通常用于监听鼠标的移动和拖动事件。

    MouseMotionAdapter类源码
    1
    2
    3
    4
    
    public abstract class MouseMotionAdapter implements MouseMotionListener {
        public void mouseDragged(MouseEvent e) {}
        public void mouseMoved(MouseEvent e) {}
    }
    

  • 使用场景:
    当你只关心 mouseMoved() 方法时,可以继承 MouseMotionAdapter 并只重写该方法。

    mouseMoved()方法
    1
    2
    3
    4
    5
    6
    7
    
    JPanel panel = new JPanel();
    panel.addMouseMotionListener(new MouseMotionAdapter() {
        @Override
        public void mouseMoved(MouseEvent e) {
            System.out.println("Mouse moved at: " + e.getX() + ", " + e.getY());
        }
    });
    

  • WindowAdapter类: 是 WindowListener 接口的适配器类,通常用于监听窗口(如 JFrame)的事件,如窗口的打开、关闭、最小化等。

    WindowAdapter类源码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    public abstract class WindowAdapter implements WindowListener {
        public void windowOpened(WindowEvent e) {}
        public void windowClosing(WindowEvent e) {}
        public void windowClosed(WindowEvent e) {}
        public void windowIconified(WindowEvent e) {}
        public void windowDeiconified(WindowEvent e) {}
        public void windowActivated(WindowEvent e) {}
        public void windowDeactivated(WindowEvent e) {}
    }
    

  • 使用场景:
    当你只关心 windowClosing() 方法时,可以继承 WindowAdapter 并只重写该方法。

    windowClosing()方法
    1
    2
    3
    4
    5
    6
    7
    
    JFrame frame = new JFrame("Window Adapter Example");
    frame.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
            System.out.println("Window is closing!");
        }
    });
    

5.3 窗口事件的处理
WindowListener 接口方法:

WindowListener 接口方法
1
2
3
4
5
6
7
8
9
public interface WindowListener {
    void windowOpened(WindowEvent e);      // 窗口打开时触发,当窗口第一次显示时调用。
    void windowClosing(WindowEvent e);     // 窗口正在关闭时触发,当窗口正在关闭时调用,通常会弹出确认对话框,询问用户是否真的要关闭窗口。
    void windowClosed(WindowEvent e);      // 窗口已关闭时触发,窗口关闭后调用,通常用于释放资源等操作。
    void windowIconified(WindowEvent e);   // 窗口最小化时触发,当窗口最小化时调用。
    void windowDeiconified(WindowEvent e); // 窗口还原时触发,当窗口还原时调用。
    void windowActivated(WindowEvent e);   // 窗口激活时触发,当窗口激活时调用,通常指的是窗口被点击、获得焦点。
    void windowDeactivated(WindowEvent e); // 窗口失去焦点时触发,当窗口失去焦点时调用,通常指的是其他窗口被激活。
}

代码当窗口关闭时,弹出对话框
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class WindowListenerExample {
    public static void main(String[] args) {
        // 创建 JFrame 窗口
        JFrame frame = new JFrame("Window Listener Example");

        // 添加 WindowListener 监听器
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                // 弹出确认对话框
                int option = JOptionPane.showConfirmDialog(
                        frame, // 父窗口
                        "系统出错了,是否确认退出?", // 对话框内容
                        "警告", // 对话框标题
                        JOptionPane.YES_NO_OPTION, // 按钮选项(是/否)
                        JOptionPane.WARNING_MESSAGE // 弹出警告图标
                );

                if (option == JOptionPane.YES_OPTION) {
                    // 如果点击“确定”,关闭窗口
                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                } else {
                    // 如果点击“取消”,不关闭窗口
                    frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
                }
            }
        });

        // 设置窗口的大小、关闭操作和可见性
        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // 默认不关闭窗口
        frame.setVisible(true);
    }
}

5.4 动作事件的处理
参考5.1,即为动作事件的处理,方法public void actionPerformed(AactionEvent e)

5.5 焦点事件的处理
FocusListener 接口方法:

FocusListener 接口方法
1
2
3
4
public abstract class FocusAdapter implements FocusListener {
    public void focusGained(FocusEvent e) {}  // 当组件获得焦点时触发
    public void focusLost(FocusEvent e) {}    // 当组件失去焦点时触发
}

代码焦点事件处理
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class FocusAdapterExample {
    public static void main(String[] args) {
        // 创建一个 JFrame 窗口
        JFrame frame = new JFrame("FocusAdapter Example");
        frame.setLayout(new FlowLayout());

        // 创建文本框和按钮
        JTextField textField = new JTextField(20);
        JButton button = new JButton("点击按钮");

        // 添加焦点监听器给文本框
        textField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                // 当文本框获得焦点时,改变按钮的文本
                button.setText("已获取焦点");
            }

            @Override
            public void focusLost(FocusEvent e) {
                // 当文本框失去焦点时,恢复按钮的文本
                button.setText("点击按钮");
            }
        });

        // 将文本框和按钮添加到窗口
        frame.add(textField);
        frame.add(button);

        // 设置窗口的大小、关闭操作和可见性
        frame.setSize(300, 150);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

6. 如何使用列表框和下拉列表框组件

  • 如何使用列表框JList
    6.1 使用数组方式创建列表框
    6.2 使用Vector方式创建列表框
    6.3 使用ListModel方式创建列表框
    6.4 列表框选取事件的处理
    6.5 列表框双击事件的处理
graph LR
A(ListModel) --> B(AbstractListModel)
B --> C(DefaultListModel)
代码如何使用列表框
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class ListBoxWithBorderExample {
    public static void main(String[] args) {
        // 创建窗口
        JFrame frame = new JFrame("List Box with Borders");
        frame.setLayout(new FlowLayout());

        // 1. 第一个列表框使用数组创建,并添加 TitledBorder
        String[] fruits = { "苹果", "香蕉", "橙子", "葡萄", "西瓜" };
        JList<String> list1 = new JList<>(fruits);
        list1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK), "你喜欢的水果"));
        list1.setPreferredSize(new Dimension(150, 150)); // 调整列表框宽度和高度

        // 2. 第二个列表框使用Vector创建,并添加 TitledBorder
        Vector<String> colors = new Vector<>(Arrays.asList("红色", "蓝色", "绿色", "黄色", "紫色"));
        JList<String> list2 = new JList<>(colors);
        list2.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK), "你最喜欢的颜色"));
        list2.setPreferredSize(new Dimension(150, 150)); // 调整列表框宽度和高度

        // 3. 第三个列表框使用ListModel创建,并添加 TitledBorder
        DefaultListModel<String> listModel3 = new DefaultListModel<>();
        listModel3.addElement("选项 A");
        listModel3.addElement("选项 B");
        listModel3.addElement("选项 C");
        JList<String> list3 = new JList<>(listModel3);
        list3.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK), "选择哪一个"));
        list3.setPreferredSize(new Dimension(150, 150)); // 调整列表框宽度和高度

        // 4. 第四个列表框用于接收移动过来的项,并添加 TitledBorder
        DefaultListModel<String> listModel4 = new DefaultListModel<>();
        JList<String> list4 = new JList<>(listModel4);
        list4.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK), "被移除到这里"));

        // 创建一个 JTextField 用来显示选择项
        JTextField textField = new JTextField(20);
        textField.setEditable(false);

        // 创建一个 JLabel 用来显示第三个列表框选择的项
        JLabel label = new JLabel("选择的项将显示在这里");
        label.setPreferredSize(new Dimension(250, 30)); // 设置标签的尺寸

        // 监听第一个列表框单击事件
        list1.addListSelectionListener(e -> {
            if (!e.getValueIsAdjusting()) { // 方法用于判断当前操作是否仍在进行,返回 true 表示操作尚未完成,返回 false 表示操作完成
                String selectedValue = list1.getSelectedValue();
                textField.setText(selectedValue); // 设置文本框显示选中的内容
            }
        });

        // 监听第二个列表框双击事件
        list2.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (e.getClickCount() == 2) {
                    String selectedValue = list2.getSelectedValue();
                    if (selectedValue != null) {
                        // 将选项从第二个列表框移到第四个列表框
                        listModel4.addElement(selectedValue);
                        // 从第二个列表框中删除该选项
                        colors.remove(selectedValue);
                        list2.setListData(colors);
                    }
                }
            }
        });

        // 监听第三个列表框选择事件,将选择的项显示到 JLabel 上
        list3.addListSelectionListener(e -> {
            if (!e.getValueIsAdjusting()) {
                String selectedValue = list3.getSelectedValue();
                label.setText("你选择了: " + selectedValue); // 设置标签显示选中的内容
            }
        });

        // 将列表框添加到窗口中
        frame.add(new JScrollPane(list1)); // 第一个列表框
        frame.add(new JScrollPane(list2)); // 第二个列表框
        frame.add(new JScrollPane(list3)); // 第三个列表框
        frame.add(new JScrollPane(list4)); // 第四个列表框
        frame.add(textField); // 显示选择项的文本框
        frame.add(label); // 显示选择项的标签

        // 设置窗口的大小、关闭操作和可见性
        frame.setSize(600, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}
  • 如何使用下拉列表框JComboBox
    6.6 使用数组和Vector创建下拉列表框
    6.7 使用ComboBoxModel创建下拉列表框
    6.8 下拉列表框的事件处理
graph LR
A(ListModel) --> B(AbstractListModel)
B --> C(JComboBoxModel)
C --> D(DefaultComboBoxModel)
代码使用下拉列表框JComboBox
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
public class FontSizeComboBoxExample {
    public static void main(String[] args) {
        // 创建窗口
        JFrame frame = new JFrame("字体大小、颜色和水果选择");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout());

        // 字符串类型的字体大小数组
        String[] fontSizes = { "12", "14", "16", "18", "20", "22", "24", "26", "28" };

        // 字符串类型的颜色数组
        Vector<String> colors = new Vector<>();
        colors.add("红");
        colors.add("黄");
        colors.add("蓝");
        colors.add("绿");
        colors.add("紫");
        colors.add("橙");

        // 字符串类型的水果数组
        String[] fruits = { "苹果", "橘子", "柿子", "葡萄", "西瓜" };

        // 1. 创建字体大小的 JComboBox,设置为可编辑模式,并将字体大小数组添加到下拉框中
        JComboBox<String> fontSizeComboBox = new JComboBox<>(fontSizes);
        fontSizeComboBox.setEditable(true);
        fontSizeComboBox.setBorder(BorderFactory.createTitledBorder("请选择你要的文字大小"));
        fontSizeComboBox.setPreferredSize(new Dimension(200, 50)); // 设置宽度为200,高度为50

        // 2. 创建颜色选择的 JComboBox,并将颜色数组添加到下拉框中
        JComboBox<String> colorComboBox = new JComboBox<>(colors);
        colorComboBox.setEditable(true);
        colorComboBox.setBorder(BorderFactory.createTitledBorder("请选择字体颜色"));
        colorComboBox.setPreferredSize(new Dimension(200, 50)); // 设置宽度为200,高度为50

        // 3. 创建水果选择的 JComboBox,使用 ComboBoxModel 创建
        ComboBoxModel<String> fruitComboBoxModel = new DefaultComboBoxModel<>(fruits);
        JComboBox<String> fruitComboBox = new JComboBox<>(fruitComboBoxModel);
        fruitComboBox.setEditable(false);
        fruitComboBox.setBorder(BorderFactory.createTitledBorder("请选择水果"));
        fruitComboBox.setPreferredSize(new Dimension(200, 50)); // 设置宽度为200,高度为50

        // 4. 创建 JLabel,显示默认提示信息
        JLabel label = new JLabel("Swing目前字形大小:");
        label.setFont(new Font("SansSerif", Font.PLAIN, Integer.parseInt(fontSizes[0]))); // 设置默认字体大小为12
        label.setForeground(Color.RED); // 默认字体颜色为红色

        // 5. 监听 JComboBox 字体大小的选择事件,更新 JLabel 的字体大小
        fontSizeComboBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 获取 JComboBox 中选中的字体大小(它是一个字符串)
                Object selectedItem = fontSizeComboBox.getSelectedItem();

                // 确保选择的是有效的字体大小
                if (selectedItem instanceof String) {
                    String fontSizeString = (String) selectedItem;

                    try {
                        // 将字符串转换为整数
                        int fontSize = Integer.parseInt(fontSizeString);

                        // 更新 JLabel 的字体大小
                        label.setFont(new Font("SansSerif", Font.PLAIN, fontSize));

                        // 更新 JLabel 的文本内容,显示新的字体大小
                        label.setText("Swing目前字形大小:" + fontSize);
                    } catch (NumberFormatException ex) {
                        // 如果字符串无法转换为数字,则处理异常
                        JOptionPane.showMessageDialog(frame, "请选择一个有效的字体大小!", "无效输入", JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
        });

        // 6. 监听 JComboBox 颜色选择的事件,更新 JLabel 的字体颜色
        colorComboBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 获取 JComboBox 中选中的颜色(它是一个字符串)
                Object selectedItem = colorComboBox.getSelectedItem();

                // 确保选择的是有效的颜色
                if (selectedItem instanceof String) {
                    String colorString = (String) selectedItem;
                    Color newColor = Color.RED; // 默认颜色

                    switch (colorString) {
                        case "红":
                            newColor = Color.RED;
                            break;
                        case "黄":
                            newColor = Color.YELLOW;
                            break;
                        case "蓝":
                            newColor = Color.BLUE;
                            break;
                        case "绿":
                            newColor = Color.GREEN;
                            break;
                        case "紫":
                            newColor = Color.MAGENTA;
                            break;
                        case "橙":
                            newColor = Color.ORANGE;
                            break;
                        default:
                            break;
                    }

                    // 更新 JLabel 的字体颜色
                    label.setForeground(newColor);
                }
            }
        });

        // 7. 监听 JComboBox 水果选择的事件,更新 JLabel 的文本
        fruitComboBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 获取 JComboBox 中选中的水果(它是一个字符串)
                Object selectedItem = fruitComboBox.getSelectedItem();

                // 确保选择的是有效的水果
                if (selectedItem instanceof String) {
                    String fruit = (String) selectedItem;
                    label.setText("您选择了水果: " + fruit);
                }
            }
        });

        // 将 JComboBox 和 JLabel 添加到窗口中
        frame.add(fontSizeComboBox);
        frame.add(colorComboBox);
        frame.add(fruitComboBox);
        frame.add(label);

        // 设置窗口的大小并显示
        frame.setSize(300, 250);
        frame.setVisible(true);
    }
}

7. 如何使用进度条、时间、滑块和分隔条组件

7.1 如何使用进度条组件JProgressBar
7.2 如何使用时间组件Timer
7.3 如何使用滑块组件JSlider
7.4 如何使用分隔条组件JSeparator

代码如何使用进度条、时间、滑块和分隔条组件
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
public class SwingExample {
    private JProgressBar progressBar;
    private JButton startButton, stopButton, restartButton;
    private JSlider slider;
    private JLabel sliderLabel;
    private JSeparator separator;
    private Timer timer;
    private int progress = 0;

    public SwingExample(JFrame frame) {
        // JFrame 设置
        frame.setTitle("Swing Example");
        frame.setSize(400, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLayout(new FlowLayout(FlowLayout.CENTER, 10, 20)); // 调整水平和垂直间距

        // JProgressBar (进度条) 设置
        progressBar = new JProgressBar(0, 100);
        progressBar.setPreferredSize(new Dimension(300, 30));
        progressBar.setStringPainted(true); // 用于显示当前进度值
        frame.add(progressBar);

        // JSeparator (分隔线) 设置
        separator = new JSeparator();
        separator.setPreferredSize(new Dimension(300, 1));
        frame.add(separator);

        // 添加垂直空白以增加 JSlider 和 JProgressBar 之间的距离
        frame.add(Box.createVerticalStrut(20)); // 这里设置增加间距

        // JSlider (滑动条) 设置
        slider = new JSlider(0, 100, 0);
        slider.setPreferredSize(new Dimension(300, 40));
        slider.setPaintTicks(true); // 显示刻度线
        slider.setPaintLabels(true); // 显示标签
        slider.setMajorTickSpacing(20); // 主刻度每 20 个单位
        slider.setMinorTickSpacing(5); // 次刻度每 5 个单位
        slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                // 滑动条值改变时更新 JLabel
                sliderLabel.setText("Slider Value: " + slider.getValue());
            }
        });
        frame.add(slider);

        // JLabel 显示滑动条值
        sliderLabel = new JLabel("Slider Value: 0");
        frame.add(sliderLabel);

        // 开始按钮
        startButton = new JButton("开始");
        startButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 启动定时器开始更新进度条
                if (timer == null || !timer.isRunning()) {
                    timer = new Timer(100, new ActionListener() { // 该定时器每隔指定的时间(delay)触发一次 ActionEvent
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            if (progress < 100) {
                                progress++;
                                progressBar.setValue(progress);
                            } else {
                                timer.stop();
                            }
                        }
                    });
                    timer.start();
                }
            }
        });
        frame.add(startButton);

        // 停止按钮
        stopButton = new JButton("停止");
        stopButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 停止定时器更新进度条
                if (timer != null && timer.isRunning()) {
                    timer.stop();
                }
            }
        });
        frame.add(stopButton);

        // 重新开始按钮
        restartButton = new JButton("重新开始");
        restartButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 重置进度条并重新开始
                if (timer != null && timer.isRunning()) {
                    timer.stop(); // 停止当前的定时器
                }
                progress = 0; // 重置进度
                progressBar.setValue(progress); // 更新进度条
                // 重新启动定时器
                timer = new Timer(100, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        if (progress < 100) {
                            progress++;
                            progressBar.setValue(progress);
                        } else {
                            timer.stop();
                        }
                    }
                });
                timer.start(); // 启动定时器
            }
        });
        frame.add(restartButton);
    }

    public static void main(String[] args) {
        // 创建 JFrame 实例
        JFrame frame = new JFrame();
        // 创建 SwingExample 实例并传入 JFrame
        SwingExample example = new SwingExample(frame);
        // 设置可见
        frame.setVisible(true);
    }
}

总结

本章重点,知道有不同的面板组件,事件处理步骤,列表框和下拉列表框,进度条、时间定时器、滑块组件。这么多内容一次记住很难,不过通过例子来理解,就比较容易了。