JavaFX教程:FXML和SceneBuilder

JavaFX教程的翻译 Vojtech Ruzicka 撰写的FXML和SceneBuilder文章

如何使用FXML标记和SceneBuilder使用JavaFX创建GUI。

JavaFX系列中的所有文章:

  1. JavaFX教程:入门
  2. JavaFX教程:世界您好!
  3. JavaFX教程:FXML和SceneBuilder
  4. JavaFX教程:基本布局
  5. JavaFX教程:高级布局
  6. JavaFX教程:CSS样式
  7. 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 { //    fx:id = "mainButton" @FXML private Button mainButton; } 

与以前的方法一样,您的字段必须是公共的或带注释的@FXML

现在我们有了按钮的链接,我们可以轻松更改其文本:

 public class MainSceneController { @FXML private Button mainButton; @FXML private void buttonClicked() { mainButton.setText("Click me again!"); } } 

场景构建器


用XML编写GUI结构可能比用Java编写更为自然(特别是如果您熟悉HTML)。 但是,它仍然不是很方便。 好消息是,有一个名为Scene Builder的官方工具可以帮助您创建用户界面。 简而言之,这是GUI的图形编辑器。



编辑器具有三个主要区域:

  1. 左侧部分显示可以拖动到中间部分的可用组件。 它还包含用户界面中所有组件的层次结构,因此您可以轻松地对其进行导航。
  2. 中间部分是基于FXML文件显示的应用程序。
  3. 右边是当前的组件检查器。 在这里,您可以编辑所选当前组件的各种属性。 在层次结构中间选择的任何组件都将显示在检查器中。

单机版


可以将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应用程序的组件。

Source: https://habr.com/ru/post/zh-CN474982/


All Articles