94、Java Swing编程入门:原理、组件与事件处理

Java Swing编程入门:原理、组件与事件处理

Java Swing编程入门:原理、组件与事件处理

1. 可插拔外观与感觉

在Java编程中,可插拔外观与感觉(Pluggable look-and-feels)代表了不同的GUI风格。使用特定风格时,只需简单地“插入”对应的外观与感觉,之后所有组件都会自动以该风格渲染。

1.1 可插拔外观与感觉的优势

  • 跨平台一致性 :能够定义在所有平台上保持一致的外观与感觉。
  • 特定平台模拟 :可以创建类似特定平台的外观与感觉,例如在Windows环境下指定Windows外观与感觉。
  • 自定义设计 :支持设计自定义的外观与感觉。
  • 运行时动态更改 :能够在运行时动态改变外观与感觉。

1.2 Java 8提供的外观与感觉

Java 8为所有Swing用户提供了多种外观与感觉,如metal和Nimbus。其中,metal外观与感觉也被称为Java外观与感觉,它是平台独立的,在所有Java执行环境中都可用,并且是默认的外观与感觉。Windows环境还可以使用Windows外观与感觉。

2. MVC架构与Swing的关系

2.1 传统MVC架构

一个可视化组件通常由三个不同方面组成:
- 外观 :组件在屏幕上的呈现方式。
- 用户交互响应 :组件对用户操作的反应方式。
- 状态信息 :与组件相关的状态信息。

传统的MVC(Model-View-Controller)架构将组件分为模型、视图和控制器三个部分:
- 模型 :对应组件的状态信息,例如复选框的模型包含一个字段,用于指示复选框是否被选中。
- 视图 :决定组件在屏幕上的显示方式,包括受模型当前状态影响的视图方面。
- 控制器 :确定组件对用户的反应方式,例如当用户点击复选框时,控制器会更改模型以反映用户的选择,进而更新视图。

2.2 Swing的改进MVC架构

虽然MVC架构及其背后的原理在概念上是合理的,但视图和控制器之间的高度分离对Swing组件并不有利。因此,Swing使用了一种改进的MVC版本,将视图和控制器组合成一个称为UI委托(UI delegate)的逻辑实体。这种方法被称为模型 - 委托架构(Model-Delegate architecture)或可分离模型架构(Separable Model architecture)。

2.3 模型 - 委托架构的优势

Swing的可插拔外观与感觉得益于其模型 - 委托架构。由于视图(外观)和控制器(感觉)与模型分离,因此可以在不影响组件在程序中使用方式的情况下更改外观与感觉,反之亦然。

2.4 支持模型 - 委托架构的组件对象

大多数Swing组件包含两个对象:
- 模型 :由接口定义,例如按钮的模型由ButtonModel接口定义。
- UI委托 :继承自ComponentUI的类,例如按钮的UI委托是ButtonUI。通常,程序不会直接与UI委托交互。

3. 组件与容器

3.1 组件与容器的概念

Swing GUI由组件和容器两个关键部分组成。实际上,所有容器也是组件,它们的区别在于用途:
- 组件 :通常是独立的可视化控件,如按钮或滑块。
- 容器 :用于容纳一组组件,是一种特殊的组件,所有组件必须包含在容器中才能显示。因此,所有Swing GUI至少有一个容器,并且容器可以嵌套,形成所谓的包含层次结构,其顶部必须是顶级容器。

3.2 组件

一般来说,Swing组件继承自JComponent类(四个顶级容器除外)。JComponent提供了所有组件共有的功能,如支持可插拔外观与感觉,并且继承了AWT的Container和Component类,因此Swing组件基于AWT组件构建并与之兼容。

Swing的所有组件由javax.swing包中的类表示,常见的组件类名如下表所示:
| 组件类名 | 说明 |
| ---- | ---- |
| JApplet | 小程序容器 |
| JButton | 按钮 |
| JCheckBox | 复选框 |
| JCheckBoxMenuItem | 复选框菜单项 |
| JColorChooser | 颜色选择器 |
| JComboBox | 下拉列表框 |
| JComponent | 组件基类 |
| JDesktopPane | 桌面面板 |
| JDialog | 对话框 |
| JEditorPane | 编辑器面板 |
| JFileChooser | 文件选择器 |
| JFormattedTextField | 格式化文本框 |
| JFrame | 窗口 |
| JInternalFrame | 内部窗口 |
| JLabel | 标签 |
| JLayer | 层 |
| JLayeredPane | 分层面板 |
| JList | 列表 |
| JMenu | 菜单 |
| JMenuBar | 菜单栏 |
| JMenuItem | 菜单项 |
| JOptionPane | 选项面板 |
| JPanel | 面板 |
| JPasswordField | 密码框 |
| JPopupMenu | 弹出菜单 |
| JProgressBar | 进度条 |
| JRadioButton | 单选按钮 |
| JRadioButtonMenuItem | 单选菜单项 |
| JRootPane | 根面板 |
| JScrollBar | 滚动条 |
| JScrollPane | 滚动面板 |
| JSeparator | 分隔符 |
| JSlider | 滑块 |
| JSpinner | 微调器 |
| JSplitPane | 分割面板 |
| JTabbedPane | 选项卡面板 |
| JTable | 表格 |
| JTextArea | 文本区域 |
| JTextField | 文本框 |
| JTextPane | 文本面板 |
| JTogglebutton | 切换按钮 |
| JToolBar | 工具栏 |
| JToolTip | 工具提示 |
| JTree | 树 |
| JViewport | 视口 |
| JWindow | 窗口 |

3.3 容器

Swing定义了两种类型的容器:
- 顶级容器 :包括JFrame、JApplet、JWindow和JDialog。这些容器不继承JComponent,但继承了AWT的Component和Container类。与Swing的其他轻量级组件不同,顶级容器是重量级的。顶级容器必须位于包含层次结构的顶部,每个包含层次结构都必须以顶级容器开始。其中,JFrame常用于应用程序,JApplet用于小程序。
- 轻量级容器 :继承自JComponent,例如JPanel是一个通用的轻量级容器,常用于组织和管理相关组件组,因为轻量级容器可以包含在其他容器中。

3.4 顶级容器的面板

每个顶级容器定义了一组面板,层次结构的顶部是JRootPane实例。JRootPane是一个轻量级容器,用于管理其他面板,并帮助管理可选的菜单栏。组成根面板的面板包括玻璃面板(glass pane)、内容面板(content pane)和分层面板(layered pane)。
- 玻璃面板 :是顶级面板,位于所有其他面板之上并完全覆盖它们。默认情况下,它是一个透明的JPanel实例,可用于管理影响整个容器的鼠标事件或在其他组件上绘制。
- 分层面板 :是JLayeredPane的实例,允许为组件指定深度值,以确定哪个组件覆盖另一个组件。
- 内容面板 :是应用程序与之交互最多的面板,因为可视化组件将添加到该面板中。默认情况下,内容面板是一个不透明的JPanel实例。

4. Swing包

Swing是一个非常大的子系统,使用了许多包,主要的包是javax.swing,任何使用Swing的程序都必须导入该包,它包含实现基本Swing组件的类,如按钮、标签和复选框。以下是Swing定义的部分包:
- javax.swing
- javax.swing.plaf.basic
- javax.swing.text
- javax.swing.border
- javax.swing.plaf.metal
- javax.swing.text.html
- javax.swing.colorchooser
- javax.swing.plaf.multi
- javax.swing.text.html.parser
- javax.swing.event
- javax.swing.plaf.nimbus
- javax.swing.text.rtf
- javax.swing.filechooser
- javax.swing.plaf.synth
- javax.swing.tree
- javax.swing.plaf
- javax.swing.table
- javax.swing.undo

5. 简单的Swing应用程序

5.1 示例代码

// A simple Swing application.
import javax.swing.*;

class SwingDemo {
    SwingDemo() {
        // Create a new JFrame container.
        JFrame jfrm = new JFrame("A Simple Swing Application");
        // Give the frame an initial size.
        jfrm.setSize(275, 100);
        // Terminate the program when the user closes the application.
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Create a text-based label.
        JLabel jlab = new JLabel(" Swing means powerful GUIs.");
        // Add the label to the content pane.
        jfrm.add(jlab);
        // Display the frame.
        jfrm.setVisible(true);
    }

    public static void main(String args[]) {
        // Create the frame on the event dispatching thread.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new SwingDemo();
            }
        });
    }
}

5.2 代码解释

  • 导入包 :程序开始时导入javax.swing包,该包包含Swing定义的组件和模型。
  • 创建JFrame容器 :使用 JFrame jfrm = new JFrame("A Simple Swing Application"); 创建一个带有标题栏、关闭、最小化、最大化和恢复按钮以及系统菜单的标准顶级窗口。
  • 设置窗口大小 :使用 jfrm.setSize(275, 100); 设置窗口的初始大小。
  • 设置默认关闭操作 :使用 jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 确保当用户关闭窗口时,整个应用程序终止。
  • 创建标签 :使用 JLabel jlab = new JLabel(" Swing means powerful GUIs."); 创建一个显示文本信息的标签。
  • 添加标签到内容面板 :使用 jfrm.add(jlab); 将标签添加到框架的内容面板中。
  • 显示窗口 :使用 jfrm.setVisible(true); 使窗口可见。
  • 在事件调度线程中创建窗口 :在 main 方法中,使用 SwingUtilities.invokeLater 方法在事件调度线程中创建 SwingDemo 对象,以避免潜在的死锁问题。

5.3 编译和运行

Swing程序的编译和运行方式与其他Java应用程序相同:
- 编译: javac SwingDemo.java
- 运行: java SwingDemo

6. 事件处理

6.1 事件处理的重要性

前面的示例展示了Swing程序的基本形式,但缺少了一个重要部分:事件处理。由于JLabel不接受用户输入,因此不需要事件处理,但其他Swing组件会响应用户输入,需要处理这些交互产生的事件。

6.2 事件处理机制

Swing使用的事件处理机制与AWT相同,称为委托事件模型(delegation event model)。许多情况下,Swing使用与AWT相同的事件,这些事件封装在java.awt.event包中,特定于Swing的事件存储在javax.swing.event包中。

6.3 示例代码

// Handle an event in a Swing program.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

class EventDemo {
    JLabel jlab;

    EventDemo() {
        // Create a new JFrame container.
        JFrame jfrm = new JFrame("An Event Example");
        // Specify FlowLayout for the layout manager.
        jfrm.setLayout(new FlowLayout());
        // Give the frame an initial size.
        jfrm.setSize(220, 90);
        // Terminate the program when the user closes the application.
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Make two buttons.
        JButton jbtnAlpha = new JButton("Alpha");
        JButton jbtnBeta = new JButton("Beta");
        // Add action listener for Alpha.
        jbtnAlpha.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                jlab.setText("Alpha was pressed.");
            }
        });
        // Add action listener for Beta.
        jbtnBeta.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent ae) {
                jlab.setText("Beta was pressed.");
            }
        });
        // Add the buttons to the content pane.
        jfrm.add(jbtnAlpha);
        jfrm.add(jbtnBeta);
        // Create a text-based label.
        jlab = new JLabel("Press a button.");
        // Add the label to the content pane.
        jfrm.add(jlab);
        // Display the frame.
        jfrm.setVisible(true);
    }

    public static void main(String args[]) {
        // Create the frame on the event dispatching thread.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new EventDemo();
            }
        });
    }
}

6.4 代码解释

  • 导入必要的包 :导入 java.awt java.awt.event javax.swing 包。
  • 创建JFrame容器 :使用 JFrame jfrm = new JFrame("An Event Example"); 创建一个带有标题的窗口。
  • 设置布局管理器 :使用 jfrm.setLayout(new FlowLayout()); 将内容面板的布局管理器设置为 FlowLayout
  • 创建按钮 :使用 JButton jbtnAlpha = new JButton("Alpha"); JButton jbtnBeta = new JButton("Beta"); 创建两个按钮。
  • 添加动作监听器 :使用 jbtnAlpha.addActionListener jbtnBeta.addActionListener 为按钮添加动作监听器,当按钮被按下时,会调用 actionPerformed 方法更新标签的文本。
  • 添加按钮和标签到内容面板 :使用 jfrm.add 方法将按钮和标签添加到内容面板中。
  • 显示窗口 :使用 jfrm.setVisible(true); 使窗口可见。
  • 在事件调度线程中创建窗口 :在 main 方法中,使用 SwingUtilities.invokeLater 方法在事件调度线程中创建 EventDemo 对象。

6.5 事件处理流程

graph LR
    A[用户操作按钮] --> B[按钮生成ActionEvent]
    B --> C[调用ActionListener的actionPerformed方法]
    C --> D[更新标签文本]

通过以上内容,我们对Java Swing编程的基本原理、组件、容器、简单应用程序和事件处理有了初步的了解。在实际开发中,可以根据需求进一步扩展和优化这些知识。

7. 深入理解组件与容器的使用

7.1 组件的布局管理

在Swing中,组件的布局管理非常重要,不同的布局管理器会影响组件在容器中的排列方式。常见的布局管理器如下:
| 布局管理器 | 说明 |
| ---- | ---- |
| BorderLayout | 分为东、西、南、北、中五个区域,组件可以放置在这些区域中。默认情况下,添加到容器的组件会被放置在中心区域。 |
| FlowLayout | 组件按照添加的顺序从左到右、从上到下排列,当一行放不下时会自动换行。 |
| GridLayout | 将容器划分为指定的行和列的网格,每个网格中放置一个组件。 |
| GridBagLayout | 功能强大但复杂的布局管理器,可以灵活地控制组件的大小和位置。 |
| BoxLayout | 可以将组件按水平或垂直方向排列。 |

例如,在前面的 EventDemo 程序中,使用了 FlowLayout 布局管理器:

jfrm.setLayout(new FlowLayout());

7.2 容器的嵌套使用

容器可以嵌套使用,形成复杂的界面布局。例如,可以在一个 JPanel 中添加多个组件,然后将这个 JPanel 添加到 JFrame 中。以下是一个简单的示例:

import javax.swing.*;
import java.awt.*;

public class NestedContainerDemo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame jfrm = new JFrame("Nested Container Example");
            jfrm.setSize(300, 200);
            jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            // 创建一个JPanel
            JPanel panel = new JPanel();
            panel.setLayout(new FlowLayout());

            // 在JPanel中添加组件
            JLabel label = new JLabel("This is a label");
            JButton button = new JButton("Click me");
            panel.add(label);
            panel.add(button);

            // 将JPanel添加到JFrame中
            jfrm.add(panel);

            jfrm.setVisible(true);
        });
    }
}

在这个示例中,首先创建了一个 JPanel ,并设置其布局管理器为 FlowLayout ,然后在 JPanel 中添加了一个标签和一个按钮,最后将 JPanel 添加到 JFrame 中。

7.3 顶级容器的使用注意事项

顶级容器(如 JFrame JApplet JWindow JDialog )在使用时需要注意以下几点:
- 标题设置 :可以在创建顶级容器时通过构造函数设置标题,如 JFrame jfrm = new JFrame("My Frame");
- 大小设置 :使用 setSize 方法设置容器的初始大小,如 jfrm.setSize(300, 200);
- 关闭操作 :使用 setDefaultCloseOperation 方法设置关闭容器时的操作,常见的有 JFrame.EXIT_ON_CLOSE (关闭窗口时退出程序)、 JFrame.DISPOSE_ON_CLOSE (关闭窗口时释放资源)等。
- 可见性设置 :使用 setVisible 方法使容器可见,如 jfrm.setVisible(true);

8. 高级组件的使用

8.1 JTable的使用

JTable 用于显示和编辑表格数据。以下是一个简单的 JTable 使用示例:

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;

public class JTableDemo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame jfrm = new JFrame("JTable Example");
            jfrm.setSize(400, 300);
            jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            // 定义表格的列名
            String[] columnNames = {"Name", "Age", "City"};
            // 定义表格的数据
            Object[][] data = {
                    {"John", 25, "New York"},
                    {"Jane", 30, "Los Angeles"},
                    {"Bob", 35, "Chicago"}
            };

            // 创建表格模型
            DefaultTableModel model = new DefaultTableModel(data, columnNames);
            // 创建JTable
            JTable table = new JTable(model);
            // 将JTable添加到滚动面板中
            JScrollPane scrollPane = new JScrollPane(table);

            jfrm.add(scrollPane);
            jfrm.setVisible(true);
        });
    }
}

在这个示例中,首先定义了表格的列名和数据,然后创建了一个 DefaultTableModel ,将列名和数据传递给它。接着创建了一个 JTable ,并将 DefaultTableModel 设置给它。最后将 JTable 添加到 JScrollPane 中,以便在表格数据较多时可以滚动查看。

8.2 JTree的使用

JTree 用于显示树形结构的数据。以下是一个简单的 JTree 使用示例:

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;

public class JTreeDemo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame jfrm = new JFrame("JTree Example");
            jfrm.setSize(300, 200);
            jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            // 创建根节点
            DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
            // 创建子节点
            DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child 1");
            DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child 2");
            // 将子节点添加到根节点
            root.add(child1);
            root.add(child2);

            // 创建JTree
            JTree tree = new JTree(root);
            // 将JTree添加到滚动面板中
            JScrollPane scrollPane = new JScrollPane(tree);

            jfrm.add(scrollPane);
            jfrm.setVisible(true);
        });
    }
}

在这个示例中,首先创建了一个根节点 root ,然后创建了两个子节点 child1 child2 ,并将它们添加到根节点中。接着创建了一个 JTree ,将根节点传递给它。最后将 JTree 添加到 JScrollPane 中。

9. 外观与感觉的定制

9.1 更改默认外观与感觉

可以在程序中更改Swing组件的外观与感觉。以下是一个更改外观与感觉为Nimbus的示例:

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class LookAndFeelDemo {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            try {
                // 设置外观与感觉为Nimbus
                UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
            } catch (Exception e) {
                e.printStackTrace();
            }

            JFrame jfrm = new JFrame("Look and Feel Example");
            jfrm.setSize(200, 150);
            jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            JButton button = new JButton("Click me");
            jfrm.add(button);

            jfrm.setVisible(true);
        });
    }
}

在这个示例中,使用 UIManager.setLookAndFeel 方法将外观与感觉设置为Nimbus。

9.2 自定义外观与感觉

如果需要自定义外观与感觉,可以创建自己的 LookAndFeel 类。这需要继承 javax.swing.plaf.metal.MetalLookAndFeel 或其他合适的 LookAndFeel 类,并覆盖相应的方法。这个过程比较复杂,需要对Swing的内部机制有深入的了解。

10. 总结

通过以上内容,我们全面了解了Java Swing编程的各个方面,包括可插拔外观与感觉、MVC架构、组件与容器的使用、简单应用程序的编写、事件处理、高级组件的使用以及外观与感觉的定制等。在实际开发中,可以根据具体需求选择合适的组件和布局管理器,灵活运用事件处理机制,创建出功能丰富、界面美观的GUI应用程序。同时,要注意在事件调度线程中创建和更新Swing组件,以避免潜在的线程安全问题。

希望这些内容能帮助你更好地掌握Java Swing编程,开启精彩的GUI开发之旅!

内容概要:本文介绍了基于贝叶斯优化的CNN-LSTM混合神经网络在时间序列预测中的应用,并提供了完整的Matlab代码实现。该模型结合了卷积神经网络(CNN)在特征提取方面的优势长短期记忆网络(LSTM)在处理时序依赖问题上的强大能力,形成一种高效的混合预测架构。通过贝叶斯优化算法自动调参,提升了模型的预测精度泛化能力,适用于风电、光伏、负荷、交通流等多种复杂非线性系统的预测任务。文中还展示了模型训练流程、参数优化机制及实际预测效果分析,突出其在科研工程应用中的实用性。; 适合人群:具备一定机器学习基基于贝叶斯优化CNN-LSTM混合神经网络预测(Matlab代码实现)础和Matlab编程经验的高校研究生、科研人员及从事预测建模的工程技术人员,尤其适合关注深度学习智能优化算法结合应用的研究者。; 使用场景及目标:①解决各类时间序列预测问题,如能源出力预测、电力负荷预测、环境数据预测等;②学习如何将CNN-LSTM模型贝叶斯优化相结合,提升模型性能;③掌握Matlab环境下深度学习模型搭建超参数自动优化的技术路线。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,重点关注贝叶斯优化模块混合神经网络结构的设计逻辑,通过调整数据集和参数加深对模型工作机制的理解,同时可将其框架迁移至其他预测场景中验证效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值