JavaFX教程的翻译
: Vojtech Ruzicka
撰写的FXML和SceneBuilder文章 。
如何使用FXML标记和SceneBuilder使用JavaFX创建GUI。
JavaFX系列中的所有文章:
- JavaFX教程:入门
- JavaFX教程:世界您好!
- JavaFX教程:FXML和SceneBuilder
- JavaFX教程:基本布局
- JavaFX教程:高级布局
- JavaFX教程:CSS样式
- JavaFX Weaver:集成JavaFX和Spring Boot应用程序
传统方式
在上一篇文章中,
我们创建了一个简单的Hello World应用程序 。
提醒一下,代码如下所示:
@Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Hello world Application"); primaryStage.setWidth(300); primaryStage.setHeight(200); InputStream iconStream = getClass().getResourceAsStream("/icon.png"); Image image = new Image(iconStream); primaryStage.getIcons().add(image); Label helloWorldLabel = new Label("Hello world!"); helloWorldLabel.setAlignment(Pos.CENTER); Scene primaryScene = new Scene(helloWorldLabel); primaryStage.setScene(primaryScene); primaryStage.show(); }
如您所见,整个用户界面都是用Java代码创建的。
这是一个非常简单的示例,但是随着您的应用程序变得越来越复杂,当您必须输入多层嵌套布局和许多组件时,生成的代码将变得非常难以理解。 但是,这还不是全部-在同一个类中,有代码同时负责结构,视觉效果和行为。
班级显然没有单一责任。 例如,将其与Web界面进行比较,其中每个页面都有明确分开的任务:
- HTML是一种结构
- CSS是视觉效果
- JavaScript是行为
FXML简介
显然,将所有代码都放在一个地方并不是一个好主意。 您需要以某种方式对其进行结构化,以便更易于理解并使其更易于管理。
实际上,为此有许多设计模式。 通常,您最终会得到一个“ Model-View-Whatever”选项-类似于“ Model View Controller”,“ Model View Presenter”或“ Model View ViewModel”。
您可以花几个小时来讨论不同选择的优缺点-在这里不要做。 更重要的是,通过JavaFx,您可以使用它们中的任何一个。
这是可能的,因为除了用户界面的过程设计之外,您还可以使用声明性XML标记。
事实证明,XML的层次结构是描述用户界面中组件层次的一种好方法。 HTML效果很好,对吧?
特定于JavaFX的XML格式称为FXML。 在其中,您可以定义所有应用程序组件及其属性,并将它们与负责管理交互的控制器关联。
下载FXML文件
那么,如何更改启动方法以与FXML一起使用?
FXMLLoader loader = new FXMLLoader(); URL xmlUrl = getClass().getResource("/mainScene.fxml"); loader.setLocation(xmlUrl); Parent root = loader.load(); primaryStage.setScene(new Scene(root)); primaryStage.show();
root代表用户界面的根组件,其他组件嵌套在其中。
load方法具有通用的返回值,因此您可以指定特定类型,而不是
Parent 。 接下来,您可以访问面向组件的方法。 但是,这会使您的代码更加脆弱。 如果您在FXML中更改根组件的类型,则该应用程序可能会在运行时停止运行,但是在编译过程中不会出现任何错误。 这是因为现在在FXML和Java FXML加载器中声明的类型不匹配。
创建FXML文件
现在我们知道了如何加载FXML文件,但是我们仍然需要创建它。 该文件必须具有扩展名.fxml。 在Maven项目中,您可以将该文件放在资源文件夹中,或者FXMLLoader可以从外部URL下载它。
创建文件后,在其第一行中输入XML声明:
<?xml version="1.0" encoding="UTF-8"?>
汇入
在将单个组件添加到文件之前,必须确保正确识别了它们。 为此,请添加导入语句。 这与在Java类中导入非常相似。 您可以照常导入单个类或使用通配符。 让我们看一个导入部分的例子:
<?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?>
好消息是,与其手动添加所有导入语句,您的IDE应该像添加它们到Java类一样帮助您添加导入。
添加组件
现在是时候添加一些组件了。 在
上一篇文章中,我们了解到每个场景只能有一个子组件。 首先,让我们添加一个简单的标签:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <Label>Hello World!</Label>
当然,标记为根组件并不是一个很现实的例子。 通常最好使用某种布局(layout),它是多个组件的容器并组织它们的排列。 我们将在本系列的后面部分介绍布局,但是现在,我们只使用一个简单的VBox,将其子级垂直放置。
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.Button?> <VBox> <Label text="Hello world!"/> <Label text="This is a simple demo application."/> <Button text="Click me!"/> </VBox>
FX命名空间
默认情况下,有几个FXML元素和属性不可用。 您需要添加一个命名空间FXML使其可用。 必须将其添加到根组件中:
<?xml version="1.0" encoding="UTF-8"?> ... <VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"> ... </VBox>
现在,您可以使用fx命名空间中的新元素。 让我们尝试向组件添加唯一标识符:
<Label fx:id="mainTitle" text="Hello world!"/>
fx:id属性是组件的唯一标识符,可用于从FXML的其他部分甚至从控制器引用组件。
剧本
我们的应用程序仍然是静态的。 有几个标签和一个按钮,但是应用程序不会动态执行任何操作。
让我们响应按钮的单击,并将标题从“单击我!”更改为“再次单击我!”。
首先要做的是为我们的按钮添加一个onAction事件处理程序。
<Button fx:id="mainButton" text="Click me!" onAction="buttonClicked()"/>
注意fx:id,这是以后将用来引用按钮的标识符。
现在,您需要提供一个将被调用以处理事件的函数。 可以在fx:脚本标记内定义它。 重要的是您可以使用不同的语言编写脚本,JavaScript,Groovy或Clojure。 让我们看一下JavaScript中的示例:

请注意,我们使用mainButton标识符引用了Button组件,该标识符的声明如下:
fx:id = "mainButton"
您还必须指出在FXML文件中使用哪种脚本语言:
<?language javascript?>
让我们看一下示例的全文:

我应该用这个吗?
上面的示例显示了如何使用
fx:id引用组件,以及如何使用JavaScript脚本添加简单行为。 那真的是你应该做的吗?
在大多数情况下,答案是否定的。 这种方法存在几个问题。 引入FXML的原因是利益分离-分离用户界面的结构和行为。 在此脚本中,与用户界面结构合并的行为再次返回。 而且,由于我们不再使用Java代码,而是使用XML,因此所有在编译时的代码检查和类型安全性都丢失了。 现在,将在运行时而不是编译时检测到应用程序中的所有问题。 该应用程序变得非常脆弱且容易出错。
添加控制器
那么,如何做才能使利益分清呢? 您可以将控制器链接到我们的FXML文件。 控制器是Java类,负责处理应用程序中的行为和用户交互。 这样,您可以返回类型安全性和编译时检查。
控制器是POJO,不应扩展或实现任何东西,也不应具有任何特殊的注释。
如何将控制器类与FXML关联? 本质上有两种选择。
在Java中
您可以自己创建控制器的实例,也可以使用其他任何创建实例的方法,例如依赖项注入。 然后只需下载
FXMLLoader即可 。
FXMLLoader loader = new FXMLLoader(); loader.setController(new MainSceneController());
在FXML中
您可以将控制器的类指定为属性
fx:controller ,该属性应位于根组件中。
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> ... </VBox>
如果在FXML中声明Controller类,则会自动为您创建。 这种方法有一个局限性-在控制器中,您需要创建一个不带参数的构造函数,以便轻松创建Controller类的新实例。
要访问自动创建的控制器的实例,可以使用FXML加载器:
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/mainScene.fxml")); MainSceneController controller = loader.getController();
调用控制器方法
现在有了控制器,您可以删除脚本并实现直接在控制器中按下按钮的逻辑:
public class MainSceneController { public void buttonClicked() { System.out.println("Button clicked!"); } }
下一步是将该方法的调用注册为
按钮的onAction事件
处理程序 。 要从我们的控制器引用方法,我们需要在方法名称前使用
#号:
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> <Label fx:id="mainTitle" text="Hello world!"/> <Label fx:id="subTitle" text="This is a simple demo application."/> <Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/> </VBox>
单击一个按钮时,它将调用
MainSceneController.buttonClicked()方法。 请记住,这仅在方法声明为public时才有效。 如果访问修饰符更加严格,则必须使用
@FXML注释对方法进行注释。
@FXML private void buttonClicked() { System.out.println("Button clicked!"); }
将组件嵌入控制器
到目前为止,我们只是打印到控制台。 如果我们想再次将按钮的文本更改为“
再次单击我 ”怎么办? 我们如何获得控制器中组件的链接?
幸运的是,这很容易。 还记得这些
fx:id属性吗?
<Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/>
JavaFX尝试自动将具有
fx:id的组件与控制器中定义的具有相同名称的字段进行匹配。
假设我们有一个上述按钮
fx:id="mainButton"
JavaFX尝试将按钮对象注入到名为
mainButton的字段中的控制器中:
public class MainSceneController {
与以前的方法一样,您的字段必须是
公共的或带注释的
@FXML 。
现在我们有了按钮的链接,我们可以轻松更改其文本:
public class MainSceneController { @FXML private Button mainButton; @FXML private void buttonClicked() { mainButton.setText("Click me again!"); } }
场景构建器
用XML编写GUI结构可能比用Java编写更为自然(特别是如果您熟悉HTML)。 但是,它仍然不是很方便。 好消息是,有一个名为Scene Builder的官方工具可以帮助您创建用户界面。 简而言之,这是GUI的图形编辑器。

编辑器具有三个主要区域:
- 左侧部分显示可以拖动到中间部分的可用组件。 它还包含用户界面中所有组件的层次结构,因此您可以轻松地对其进行导航。
- 中间部分是基于FXML文件显示的应用程序。
- 右边是当前的组件检查器。 在这里,您可以编辑所选当前组件的各种属性。 在层次结构中间选择的任何组件都将显示在检查器中。
单机版
可以将Scene Builder作为独立的应用程序
下载 ,可用于编辑FXML文件。
与IntelliJ IDEA集成
另外,Scene Builder提供与IDE的集成。
在IntelliJ IDEA中,您可以右键单击任何FXML文件,然后在SceneBuilder中选择“打开”菜单选项。
另外,IntelliJ IDEA可将SceneBuilder直接集成到IDE中。 如果您在IDEA中打开FXML文件,则屏幕底部将显示两个选项卡
对于每个FXML文件,您可以在直接编辑FXML文件或通过SceneBuilder编辑之间轻松切换。

在IntelliJ IDEA中,您可以配置SceneBuilder可执行文件的位置:
Settings → Languages & Frameworks → JavaFX → Path to SceneBuilder
接下来是什么
在本系列的下一篇文章中,我们将讨论一些基本布局,这些布局可用于组织JavaFX中的GUI应用程序的组件。