JavaFX FXML是一种 XML 格式,使您能够以类似于在 HTML 中编写 Web GUI 的方式编写 JavaFX GUI。因此, FXML使您能够将 JavaFX 布局代码与应用程序代码的其余部分分开。这会清理布局代码和应用程序代码的其余部分。
FXML 既可用于构成整个应用程序 GUI 的布局,也可仅用于构成应用程序 GUI 的一部分,例如表单、选项卡、对话框等的一部分的布局。
JavaFX FXML 示例
开始学习 JavaFX FXML 的最简单方法是查看 FXML 示例。下面是一个构成简单 JavaFX GUI 的 FXML 示例:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<VBox>
<children>
<Label text="Hello world FXML"/>
</children>
</VBox>
此示例定义了一个包含单个Label作为子元素的VBox 。该组件是一个 JavaFX 布局组件。只是在 GUI 中显示一个文本。如果您还不了解所有 JavaFX 组件,请不要担心。一旦你开始和他们一起玩,你会的。 VBoxLabel
FXML 文档的第一行是标准的 XML 文档的第一行。
以下两行是导入语句。在 FXML 中,您需要导入要使用的类。必须导入 FXML 中使用的 JavaFX 类和核心 Java 类。
在导入语句之后,您将获得 GUI 的实际组成。声明了一个组件VBox,并在其children属性内声明了一个Label组件。结果是Label实例将被添加到实例的children属性中VBox。
加载 FXML 文件
为了加载 FXML 文件并创建文件声明的 JavaFX GUI 组件,您使用 FXMLLoader( javafx.fxml.FXMLLoader) 类。这是一个完整的 JavaFX FXML 加载示例,它加载 FXML 文件并返回其中声明的 JavaFX GUI 组件:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.net.URL;
public class FXMLExample extends Application{
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(new URL("file:///C:/data/hello-world.fxml"));
VBox vbox = loader.<VBox>load();
Scene scene = new Scene(vbox);
primaryStage.setScene(scene);
primaryStage.show();
}
}
要使此示例正常工作,FXML 文件必须位于C:\data\hello-world.fxml. 如您所见,文件的位置是通过该setLocation()方法设置的。根 GUI 组件(VBox对象)通过该load()方法获得。
在 FXML 中导入类
为了在 FXML 中使用 Java 类,无论是 JavaFX GUI 组件还是常规 Java 类,都必须在 FXML 文件中导入该类。FXML 导入语句如下所示:
<?import javafx.scene.layout.VBox?>
此 FXML 导入语句导入类javafx.scene.layout.VBox。
在 FXML 中创建对象
FXML 可以创建 JavaFX GUI 对象以及非 JavaFX 对象。有几种方法可以在 FXML 中创建对象。在以下部分中,我们将看到这些选项是什么。
通过 FXML 元素和无参数构造函数创建对象
在 FXML 中创建对象的最简单方法是通过 FXML 文件中的 FXML 元素。FXML 中使用的元素名称与没有包名称的 Java 类名称相同。通过 FXML 导入语句导入类后,可以将其名称用作 FXML 元素名称。
在以下示例中,元素名称VBox和Label是有效的,因为这两个类是在 FXML 文件的前面使用 import 语句声明的:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<VBox>
<children>
<Label text ="Hello world FXML"/>
</children>
</VBox>
要使用这样的 FXML 元素创建对象,需要创建对象的类具有无参数构造函数。
通过 valueOf() 方法创建对象
在 FXML 中创建对象的另一种方法是在要创建对象的类中调用静态valueOf()方法。通过方法创建对象的方法是在 FXML 元素中valueOf()插入一个 属性。value这是一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jenkov.javafx.MyClass?>
<MyClass value="The Value"/>
以下是相应的MyClass需要如何寻找它的工作:
public MyClass {
public static MyClass valueOf(String value) {
return new MyClass(value);
}
private String value = null;
public MyClass(String value) {
this.value = value;
}
}
请注意将Java 字符串作为参数 的静态valueOf()方法。当它看到FXML 文件中的元素时,会调用此方法。该方法返回的对象是插入到由 FXML 文件组成的 GUI 中的对象。上面的 FXML 不包含除元素之外的任何其他元素 ,但它可以。 FXMLLoaderMyClassvalueOf()MyClass
请记住,该valueOf()方法返回的任何对象都将在对象图(组合 GUI)中使用。如果返回的对象不是包含该valueOf() 方法的类的实例,而是某个其他类的实例,那么该对象仍将在对象图中使用。元素名称仅用于查找包含valueOf()方法的类(当 FXML 元素包含 value属性时)。
通过工厂方法创建对象
从某种意义上说,valueOf()方法也是一种基于String参数创建对象的工厂方法。但是 - 您也可以FXMLLoader调用其他工厂方法而不是 valueOf()方法。
要调用另一个工厂方法来创建对象,您需要插入一个fx:factory属性。属性的值fx:factory应该是要调用的工厂方法的名称。这是一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jenkov.javafx.MyClass?>
<MyClass fx:factory="instance"/>
为了使上述 FXML 示例正常工作,MyClass 类应如下所示:
public MyClass {
public static MyClass instance() {
return new MyClass();
}
}
注意instance()方法。此方法是从fx:factory 上面 FXML 片段中的属性引用的。
请注意,工厂方法必须是无参数方法才能从fx:factory属性调用它。
FXML 中的属性
一些 JavaFX 对象具有属性。事实上,他们中的大多数人都这样做。您可以通过两种方式设置属性的值。第一种方法是使用 XML 属性来设置属性值。第二种方法是使用嵌套的 XML 元素来设置属性值。
为了更好地理解如何在 FXML 元素中设置属性,让我们看一个例子:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<VBox spacing="20">
< children>
<Label text="Line 1"/>
<Label text="Line 2"/>
</children>
</VBox>
此示例显示 3 个属性示例。第一个例子是元素中的spacing属性 。VBox属性中设置的值spacing作为参数传递给基于 元素 创建setSpacing()的对象的方法。VBoxVBox
第二个例子是children嵌套在元素内的VBox元素。该元素对应于类的getChildren()方法VBox。嵌套在元素内的children元素将转换为 JavaFX 组件,这些组件将添加到从 父元素 表示getChildren()的对象的方法获得的集合中。VBoxVBox
第三个例子是text嵌套Label在children. 属性的值text将作为参数传递给由 元素 创建的对象的setText()属性。LabelLabel
属性名称匹配
FXML 将“属性”视为通过 getter 和 setter 访问的成员变量。例如 getText()和setText()。
从上一节的示例中可以看出,JavaFX 类的属性名称通过以下方式与属性和元素名称匹配:
- 删除属性名称中的任何 get/set。
- 将属性名称的第一个剩余字符转换为小写。
因此,getter 方法getChildren将首先简化为Children ,然后简化为children。同样,setter 方法setText将被简化为Text,然后为text。
默认属性
JavaFX 组件可以具有默认属性。这意味着,如果 FXML 元素包含未嵌套在属性元素内的子元素,则假定这些子元素属于默认属性。
让我们看一个例子。该类VBox具有该children属性作为默认属性。这意味着我们可以省略children元素。因此,这个 FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<VBox spacing="20">
< children>
<Label text="Line 1"/>
<Label text="Line 2"/>
</children>
</VBox>
可以缩短为:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<VBox spacing="20">
<Label text="Line 1"/>
<Label text="Line 2"/>
</VBox>
然后假定这两个Label元素属于 的默认属性VBox,即children属性。
默认属性用 JavaFX 注释标记,@DefaultProperty(value=”propertyName”) 其中值是应该是默认属性的属性的名称。例如, @DefaultProperty(value=”children”)声明将使该children属性成为默认属性。
FXML 命名空间
FXML 有一个命名空间,您可以在 FXML 文件的根元素上设置。某些 FXML 属性(如属性)需要 FXML 命名空间fx:id(请参阅本 FXML 教程的下一部分)。
在 FXML 文件的根元素上设置 FXML 命名空间如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml">
</VBox >
FXML 命名空间由属性声明声明xmlns:fx=”http://javafx.com/fxml"。
FXML 元素 ID
您可以将 ID 分配给 FXML 元素。这些 ID 可用于引用 FXML 文件中其他位置的 FXML 元素。为 FXML 元素指定 ID 是通过idFXML 命名空间中的属性完成的。以下是为 FXML 元素指定和 ID 的示例:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<VBox xmlns:fx="http: //javafx.com/fxml">
<Label fx:id="label1" text="Line 1"/>
</VBox>
注意元素fx:id=”label1”中 的属性声明。Label此属性声明该Label元素的 ID。Label现在可以通过label1FXML 文档中其他地方的 ID 引用这个特定元素。例如,此 ID 可用于引用 CSS 中的 FXML 元素。您将在本 FXML 教程后面看到通过 ID 引用 FXML 元素的示例。
FXML 事件处理程序
可以从定义 JavaFX 对象的 FXML 文件中对 JavaFX 对象设置事件处理程序。您可能更喜欢从 Java 代码中设置高级事件处理程序,但对于简单的事件处理程序,从 FXML 中设置它们可能没问题。
为了定义一个事件处理程序,您需要使用一个script元素。以下是 FXML 脚本元素的外观:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<VBox xmlns:fx="http://javafx.com/fxml">
<Label fx:id="label1" text="Button not clicked"/>
<Button fx:id="button1" text="Click me!" onAction="reactToClick()"/>
<fx:script>
function reactToClick() {
label1.setText("Button clicked");
}
</fx:script>
</VBox>
这个例子展示了两个有趣的 FXML 概念。第一个概念是从 FXML 中向 JavaFX 组件添加事件侦听器。该元素通过其属性Button声明一个事件监听器 。onAction属性值声明了对在 FXML 文件下方的元素 reactToClick()中定义的函数的调用。script
第二个概念是通过 FXML 文件中的 ID 引用 JavaFX 组件。在 元素中reactToClick()声明的方法中,元素通过其 ID 引用,通过以下语句: scriptLabellabel1
label1.setText("Button clicked");
onAction事件监听器属性对应于组件 的onAction事件 Button。您也可以通过 Java 代码通过Button setOnAction()方法设置此事件侦听器。您也可以在 FXML 中为其他事件设置侦听器,方法是将相应 JavaFX 组件中的事件侦听器方法与 FXML 属性匹配,使用与其他属性相同的名称匹配规则(参见前面的属性名称匹配部分)。
FXML CSS 样式
可以对在 FXML 文件中声明的 JavaFX 组件进行样式设置。您可以通过 style在 FXML 元素中嵌入一个元素来实现。以下是在 FXML 文件中设置 JavaFX 按钮的 CSS 样式示例:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox xmlns:fx="http://javafx.com/fxml">
<Button text="Click me!"/ onAction="reactToClick()">
<style>
-fx-padding: 10;
-fx-border-width: 3;
</style>
</Button>
</VBox>
此示例将-fx-paddingCSS 属性设置为 10,将-fx-border-width属性设置为 3。由于style元素嵌套在button元素内部,因此这些 CSS 样式将应用于该button元素。
FXML 控制器类
您可以为 FXML 文档设置控制器类。FXML 控制器类可以将 FXML 文件中声明的 GUI 组件绑定在一起,使控制器对象充当中介(设计模式)。
有两种方法可以为 FXML 文件设置控制器。设置控制器的第一种方法是在 FXML 文件中指定它。第二种方法是在 FXMLLoader用于加载 FXML 文档的实例上设置控制器类的实例。此 JavaFX FXML 教程将在以下部分中显示这两个选项。
在 FXML 中指定控制器类
fx:controller 控制器类是使用属性 在 FXML 文件的根元素中指定的。以下是在 FXML 中指定控制器的示例:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.jenkov.javafx.MyFxmlController" >
<Button text="Click me!"/ onAction="reactToClick()">
</Button>
</VBox>
注意fx:controller根元素(VBox元素)中的属性。此属性包含控制器类的名称。加载 FXML 文件时会创建此类的一个实例。为此,控制器类必须有一个无参数的构造函数。
在 FXMLLoader 上设置控制器实例
在 上设置控制器实例时,FXMLLoader您必须首先创建控制器类的实例,然后将该实例设置在FXMLLoader. 这是在实例上设置控制器实例的FXMLLoader示例:
MyFxmlController controller = new MyFxmlController();
FXMLLoader loader = new FXMLLoader();
loader.setController(controller);
将 JavaFX 组件绑定到控制器字段
您可以将 FXML 文件中的 JavaFX 组件绑定到控制器类中的字段。要将 JavaFX 组件绑定到控制器类中的字段,您需要为 JavaFX 组件的 FXML 元素提供一个fx:id 属性,该属性具有控制器字段的名称以将其绑定为值。这是一个示例控制器类:
public class MyFxmlController {
public Label label1 = null;
}
这是 FXML 文件,其中一个Label元素绑定到label1控制器类的字段:
<VBox xmlns:fx="http://javafx.com/fxml" >
<Label fx:id="label1" text="Line 1"/>
</VBox>
请注意fx:id属性的值如何具有label1与它应该绑定到的控制器类中的字段名称相同的值。
在控制器中引用方法
可以从 FXML 引用控制器实例中的方法。例如,您可以将 JavaFX GUI 组件的事件绑定到控制器的方法。这是一个将 JavaFX 组件的事件绑定到控制器中的方法的示例:
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="com.jenkov.javafx.MyFxmlController" spacing="20">
<children>
<Label fx:id="label1" text="Line 1"/>
<Label fx:id="label2" text="Line 2"/>
<Button fx:id="button1" text="Click me!" onAction="#buttonClicked"/>
</children>
</VBox>
此示例将onAction事件绑定Button到控制器类中的方法buttonClicked 。下面是控制器类必须如何启用事件绑定:
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class MyFxmlController {
@FXML
public void buttonClicked(Event e){
System.out.println("Button clicked");
}
}
注意方法@FXML上方的注释buttonClicked。此注释将方法标记为 FXML 绑定的目标。另请注意,buttonClicked在 FXML 文件中引用了该名称。
从 FXMLLoader 获取控制器实例
FXMLLoader实例加载 FXML 文档后,您可以通过该FXMLLoader getController()方法获取对控制器实例 的引用。这是一个例子:
MyFxmlController controllerRef = loader.getController();