JavaFx:生成布局 ViewBinding,告别 @FXML 注解

在做一个桌面工具的时候用到了 JavaFx,和 Android 类似的是 JavaFx 可以在 xml 中写布局。不同点在于 JavaFx 通过 fx:controller 属性指定控制器类或者在加载布局时指定控制器类才可以在控制类中通过 @FXML 注解获取到布局中的 View,注解这一点类似 Android 以前使用的 ButterKnife 注解库。虽然 @FXML 注解获取 View 的方式并不复杂,但是如果页面复杂有很多控件,@FXML 注解在控制器类中就会占用很多行了,开发体验并不好。

作为 Android 开发者,用过 ViewBinding 的估计都不想回到 findViewById 或者使用 ButterKnife 注解的时代了,所以参考 Android ViewBinding,给 JavaFx 项目自定义了一个 gradle task 来生成布局对应的 ViewBinding 类,这样就不用再手动去写 @FXML 注解了,直接使用生成的 ViewBinding 类即可。

这里先贴上 demo 项目地址:JavaFxViewBinding

实现 ViewBinding 生成的 gradle task 的时候主要考虑了以下2点:

1、如果 fxml 布局中使用 fx:controller 指定了控制器类则不生成 ViewBinding 类(因为 ViewBinding 类的作用就类似于一个只包含了控件的控制器);

2、ViewBinding task 增量构建,如果 fxml 没有修改或者生成的 ViewBinding 类没有被删除,不用重新处理;

这里就不贴 ViewBinding task 代码了,详情可以看:FxmlViewBindingTask.kt

下面贴一下默认的使用 @FXML 的代码和使用 ViewBinding 后的代码,方便对二者进行比较:

1、默认 @FXML 用法:

布局:hello-view.fxml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER"
    spacing="20.0"
    xmlns:fx="http://javafx.com/fxml"
    fx:controller="cbfg.vbinding.HelloController">
    <!-- 关键:fx:controller 指定控制器类 -->
    <padding>
        <Insets bottom="20.0"
            left="20.0"
            right="20.0"
            top="20.0" />
    </padding>

    <Label fx:id="welcomeText" />
    <Button text="ViewBinding Demo"
        onAction="#onHelloButtonClick" />
</VBox>

控制器类:HelloController.kt

kotlin 复制代码
package cbfg.vbinding

import javafx.fxml.FXML
import javafx.fxml.FXMLLoader
import javafx.scene.Scene
import javafx.scene.control.Label
import javafx.stage.Stage

class HelloController {
    //通过 @FXML 注解获取布局中的 View:
    @FXML
    private lateinit var welcomeText: Label

    //通过 @FXML 注解 fxml 布局中使用的点击方法:
    @FXML
    private fun onHelloButtonClick() {
        welcomeText.text = "Welcome to JavaFX Application!"
    }

    companion object {
        fun show(stage: Stage) {
            val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource("hello-view.fxml"))
            val scene = Scene(fxmlLoader.load(), 320.0, 240.0)
            stage.title = "Hello!"
            stage.scene = scene
            stage.show()
        }
    }
}

2、ViewBinding 用法:

布局:view-binding.fxml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER"
    spacing="20.0"
    xmlns:fx="http://javafx.com/fxml">
    <!-- 关键:不要使用 fx:controller 指定控制器类 -->
    <padding>
        <Insets bottom="20.0"
            left="20.0"
            right="20.0"
            top="20.0" />
    </padding>

    <Label fx:id="welcomeText" />
    <Button fx:id="btnHello"
        text="Hello!" />
</VBox>

控制器类:ViewBindingController.kt

kotlin 复制代码
package cbfg.vbinding

import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.Scene
import javafx.stage.Stage

class ViewBindingController {
    //fxml 布局与生成的 ViewBinding 类名称及路径对应关系:
    //resources/a.fxml -> ABinding
    //resources/cbfg/vbinding/view-binding.fxml -> cbfg.vbinding.ViewBindingBinding
    private val binding = ViewBindingBinding.bind()
    //也可以通过传入 fxml 布局路径进行绑定:
    //private val binding= ViewBindingBinding.bind("/cbfg/vbinding/view-binding.fxml")

    init {
        binding.btnHello.onAction = EventHandler<ActionEvent> {
            binding.welcomeText.text = "Welcome to ViewBinding!"
        }
    }

    companion object {
        fun show(stage: Stage) {
            val viewBindingLayoutView = ViewBindingController().binding.root
            val scene = Scene(viewBindingLayoutView, 320.0, 240.0)
            stage.title = "ViewBinding 示例页"
            stage.scene = scene
            stage.show()
        }
    }
}

生成的 ViewBinding 类:

以上的例子比较简单,布局中定义的控件不多,如果是复杂的布局能更好体现出 ViewBinding 的好处,避免手写很多 @FXML 注解。其它的就不多说了。在写这篇文章的时候,我已经转向使用 Compose Desktop 了,就让这篇文章作为我使用过 JavaFx 开发项目的历史见证吧!

相关推荐
要开心吖ZSH9 小时前
一文搞懂:JDK 21 虚拟线程 vs N种异步方案,到底该怎么选?
java·数据库·jdk·虚拟线程
guodingdingh14 小时前
软件开发工作问题总结0718
java·开发语言·数据库
咖啡八杯15 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
优橙教育15 小时前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
糖果店的幽灵16 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
腻害兔17 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
碎光拾影18 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
Miao1213118 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
腻害兔19 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程
CRMEB系统商城20 小时前
开源自建还是SaaS订阅?算一笔3年经济账
java·大数据·开发语言·开源