☕ 第十四章 图形用户界面

14.5 JavaFX

📄 一句话概括

图形界面家族的第二代成员:Swing 是老一辈,JavaFX 是新一代(更漂亮、支持 CSS/动画/多媒体/FXML 界面文件)。套路和 Swing 高度同构:Stage 舞台 → Scene 场景 → Node 节点三层,对照 Swing 的套娃三层一翻就懂。本节定位是"会认、会跑一个最小程序"——概念新朋友,思想全老熟人。


一、JavaFX 是谁:Swing 的新一代兄弟

Swing(14.1~14.4) JavaFX(本节)
出生 1998,AWT 的升级 2008,Swing 的接班人
外观 传统桌面风 现代风,支持 CSS 美化
加分项 老项目遍地 动画、多媒体、FXML 界面文件、属性绑定
现状 JDK 自带,随到随用 JDK 8 内置;JDK 11 起被移出 JDK,要单独装 OpenJFX
⚠️ 波仔的 JDK 17 要先"接人"

JDK 17 不自带 JavaFX。直接跑会报:"JavaFX runtime components are missing"(缺运行组件)。两条路: 1. IDEA 装库:File → Project Structure → Libraries → + → 下载/导入 OpenJFX(gluon 官网下 JavaFX SDK 解压,指到 lib 文件夹); 2. 加运行参数:Run Configuration → VM options 填 --module-path <SDK的lib路径> --add-modules javafx.controls。 配好一次终身受用;如果教材配套项目已经带好了,直接跑 Demo30 验收。

二、三层结构:舞台装场景,场景装节点

JavaFX 把界面拆成三层,跟 Swing 套娃一一对号:

JavaFX 职责 Swing 对应(14.1)
Stage 舞台=操作系统给的窗口 JFrame
Scene 场景=窗口里的一块幕布,一次放一块 内容面板
Node 节点=幕布上的演员(按钮/文本/布局) JButton 等组件

规矩:Stage 装 Scene,Scene 装根节点,根节点再装其他节点——组件不能直接 add 给 Stage/Scene,必须先装进一个布局根节点。

三、Application 套路:四步最小骨架

写 JavaFX 程序 = 继承 Application 类、重写 start、launch 启动:

  1. 写个类 extends Application(4.1 继承老规矩);
  2. 重写 start(Stage stage)——搭界面的代码全在这(用 @Override 标注,9.4);
  3. main 里调 launch(args)——它内部会自动造出你的对象、依次调 init() → start();
  4. 关窗后框架自动调 stop(),程序谢幕。

关键认知:init() / start() / stop() 都不是你调的,是框架代调的(模板方法,3.5 this 那章"父类定流程子类填内容"的思路)。你只管重写,别在 main 里自己 new Demo30().start(...) 抢戏——那样 launch 的配套环境根本没建起来,直接报错。

四、控件与布局对照表:换名字不换思想

Label lbl = new Label("还没点");        // ↔ JLabel
Button btn = new Button("点我");        // ↔ JButton
TextField txt = new TextField();        // ↔ JTextField(getText/setText 通用)
PasswordField pf = new PasswordField(); // ↔ JPasswordField

VBox root = new VBox(10, lbl, btn);     // 垂直排列,间距 10 ↔ FlowLayout 纵向版
HBox row = new HBox(10, lbl, btn);      // 水平排列
BorderPane bp = new BorderPane();       // ↔ BorderLayout,同样东南西北中
bp.setTop(node);  bp.setCenter(node);   // 摆位用 setTop/setBottom/setLeft/setRight/setCenter

事件也一样,只换了名字:btn.setOnAction(e -> ...) ↔ Swing 的 addActionListener——Lambda 照样上岗,e 照样是事件对象。思想零成本迁移。

五、动手:Demo30 JavaFX 第一扇窗

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Demo30 extends Application {
    @Override
    public void start(Stage stage) {
        Label lbl = new Label("还没点");
        Button btn = new Button("点我");
        btn.setOnAction(e -> lbl.setText("你点过了!"));   // 门铃:14.4 同款 Lambda

        VBox root = new VBox(10, lbl, btn);        // 根节点:垂直摆两样
        Scene scene = new Scene(root, 300, 200);   // 场景装根节点,300×200

        stage.setTitle("JavaFX 第一扇窗");
        stage.setScene(scene);                     // 舞台装场景
        stage.show();                              // ↔ setVisible(true)
    }

    public static void main(String[] args) {
        launch(args);
    }
}

跑起来:一个现代风格的窗口,点按钮标签变字——Swing 十四节学的每一招,在这里都有对应的影子。

🧪 实验 1:不配 OpenJFX 直接跑

报 JavaFX runtime components are missing——亲眼见一次这个错,按第一节的两条路配好,再跑就通了。配置错误是本节 90% 的翻车点。

🧪 实验 2:把 setOnAction 换成匿名内部类

btn.setOnAction(new EventHandler<ActionEvent>() {...})——长得跟 14.4 写法二一个模子。EventHandler 也是单方法函数式接口,所以最终都能瘦身成 Lambda。

🧪 实验 3:换布局

把 VBox 换成 HBox:两个控件横着排;换 BorderPane 并 bp.setTop(lbl); bp.setCenter(btn);——标签顶上、按钮居中。布局师傅还是那批思路,只是按"流"改名为"Pane"。

六、五个常见误区

⚠️ 误区 1:JDK 17 直接跑 JavaFX 代码

必报 missing components。JDK 11 起 JavaFX 不再内置——先配 OpenJFX(库 + VM 参数),再谈运行。

⚠️ 误区 2:main 里自己 new 出对象调 start()

start 的舞台、场景环境都是 launch 建的。绕过 launch 直接调 start,内部资源全没就位,各种莫名报错。口诀:launch 负责"叫人",start 只管"演"。

⚠️ 误区 3:把控件直接塞给 Stage 或 Scene

Stage 只收 Scene,Scene 只收一个根节点。控件要先装进 VBox/HBox 等布局根节点,再 new Scene(root, ...)。

⚠️ 误区 4:把界面操作放到别的线程里做

报 Not on FX application thread——JavaFX 界面只认自己的 UI 线程(对应 Swing 的 EDT)。第十二章的老道理在 GUI 里一样铁:界面只由 UI 线程摸,后台线程算完结果要交回主线程刷新(进阶再展开)。

⚠️ 误区 5:以为学了 JavaFX 就该把 Swing 全忘了

两者是并存关系:Swing 老项目遍地(IDEA 本体就是 Swing 写的),JavaFX 主打新开发。教材讲 JavaFX 是让你"多认识一门",思想是同一套。

七、自测题

❓ Q1:JavaFX 三层结构是什么?各对应 Swing 的谁?

Stage 舞台=窗口(JFrame)、Scene 场景=幕布(内容容器)、Node 节点=演员(组件)。规矩:舞台装场景,场景装根节点。

❓ Q2:JDK 17 跑 JavaFX 报 missing components 怎么办?

JDK 11 起不内置 JavaFX:装 OpenJFX SDK(IDEA 加库)+ 运行参数 --module-path <lib路径> --add-modules javafx.controls。

❓ Q3:start() 是你自己调的吗?谁在什么时候调?

不是。launch(args) 内部自动建环境后,由框架依次调 init() → start();关窗时调 stop()。你只管重写。

❓ Q4:JavaFX 里给按钮挂事件的代码怎么写?和 Swing 哪句话对应?

btn.setOnAction(e -> ...),对应 Swing 的 btn.addActionListener(e -> ...)——EventHandler 也是函数式接口,Lambda 照用。

❓ Q5:(毕业题)把 Demo30 升级成 JavaFX 版迷你登录:TextField 用户名、PasswordField 密码、Button 登录,对了弹"登录成功",错了标签显示"用户名或密码错误"。

参考骨架:

TextField name = new TextField();
PasswordField pwd = new PasswordField();
Label msg = new Label();
Button btn = new Button("登录");
btn.setOnAction(e -> {
    if ("admin".equals(name.getText()) && "123456".equals(pwd.getText())) {
        msg.setText("登录成功");
    } else {
        msg.setText("用户名或密码错误");
    }
});
VBox root = new VBox(10, name, pwd, btn, msg);

自检:控件都装进根节点了吗?equals 而不是 ==(6.2)?消息用标签显示(也可以试 Alert 弹窗)?——到此 GUI 两大门派都摸过了。

八、下一步学什么


相关笔记