JavaFX FXML

时间:2020-01-09 10:36:36  来源:igfitidea点击:

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。 " VBox"组件是JavaFX布局组件。 "标签"仅在GUI中显示文本。如果我们还不了解所有JavaFX组件,请不要担心。一旦开始与他们一起玩,我们将可以使用。

FXML文档中的第一行是XML文档的标准第一行。

以下两行是import语句。在FXML中,我们需要导入要使用的类。必须导入FXML中使用的JavaFX类和核心Java类。

在导入语句之后,我们具有GUI的实际组成。声明了" VBox"组件,并在其" children"属性内声明了一个" Label"组件。结果是Label实例将被添加到VBox实例的children属性中。

加载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 import语句导入类后,可以将其名称用作FXML元素名称。

在下面的示例中,元素名称VBoxLabel是有效的,因为这两个类是在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()"方法。通过" valueOf()"方法创建对象的方法是在FXML元素中插入" value"属性。这是一个例子:

<?xml version="1.0" encoding="UTF-8"?>
<?import com.Hyman.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 String作为参数的static" valueOf()"方法。当FXMLLoader在FXML文件中看到MyClass元素时,将调用此方法。由valueOf()方法返回的对象是插入到FXML文件中组成的GUI中的对象。上面的FXML除了MyClass元素外,不包含其他任何元素,但是可以包含。

请记住,由" valueOf()"方法返回的任何对象都将在对象图(组成的GUI)中使用。如果返回的对象不是包含valueValue()方法的类的实例,而是其他某个类的实例,则该对象仍将在对象图中使用。元素名称仅用于查找包含" valueOf()"方法的类(当FXML元素包含" value"属性时)。

通过工厂方法创建对象

从某种意义上说," valueOf()"方法也是一种基于String参数创建对象的工厂方法。但是我们也可以使用FXMLLoader来调用除valueOf()方法之外的其他工厂方法。

要调用另一个工厂方法来创建对象,我们需要插入fx:factory属性。 fx:factory属性的值应该是要调用的工厂方法的名称。这是一个例子:

<?xml version="1.0" encoding="UTF-8"?>
<?import com.Hyman.javafx.MyClass?>

<MyClass fx:factory="instance"/>

MyClass类应类似于上面的FXML示例:

public MyClass {
    public static MyClass instance() {
        return new MyClass();
    }
}

注意instance()方法。上面的FXML代码段中的fx:factory属性引用了此方法。

注意,工厂方法必须是无参数方法,才能从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个属性示例。第一个例子是VBox元素中的spacing属性。在spacing属性中设置的值作为参数传递给基于VBox元素创建的VBox对象的setSpacing()方法。

第二个例子是嵌套在VBox元素内的children元素。该元素对应于VBox类的getChildren()方法。嵌套在child元素内的元素将转换为JavaFX组件,这些组件将添加到从父VBox元素表示的VBox对象的getChildren()方法获得的集合中。

第三个示例是嵌套在"孩子"内部的两个"标签"元素的"文本"属性。文本属性的值将作为参数传递给由标签元素创建的标签对象的setText()属性。

属性名称匹配

FXML认为"属性"是通过getter和setter访问的成员变量。例如。 getText()和setText()。

从上一节的示例中可以看到,JavaFX类的属性名称通过以下方式与属性和元素名称匹配:

  • 删除属性名称中的所有获取/设置。
  • 将属性名称的第一个剩余字符转换为小写。

因此,getter方法" getChildren"将首先被简化为" Children",然后将其简化为" children"。类似地,setter方法的setText方法将简化为Text,然后简化为text。

默认属性

JavaFX组件可以具有默认属性。这意味着,如果FXML元素包含未嵌套在property元素内的子元素,则假定这些子元素属于默认属性。

让我们来看一个例子。 VBox类具有child属性作为默认属性。这意味着我们可以省略" 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")声明会将child属性设为默认属性。

FXML命名空间

FXML具有可以在FXML文件的根元素上设置的名称空间。一些FXML属性(例如fx:id属性)需要FXML名称空间(请参阅本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

我们可以为FXML元素分配ID。这些ID可用于引用FXML文件中其他位置的FXML元素。通过FXML名称空间的id属性指定FXML元素的ID。这是为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"。此属性声明该"标签"元素的ID。现在,可以通过FXML文档中其他位置的ID" label1"引用此特定的" Label"元素。例如,此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属性声明事件监听器。该属性值声明对" reactToClick()"函数的调用,该函数在FXML文件下方的" script"元素中定义。

第二个概念是通过FXML文件中的JavaFX组件ID对其进行引用。在script元素中声明的reactToClick()方法内部,通过以下语句通过其ID label1引用Label元素:

label1.setText("Button clicked");

onAction事件监听器属性对应于Button组件的onAction事件。我们也可以通过Java代码,通过Button``setOnAction()方法来设置此事件侦听器。我们也可以使用与其他属性相同的名称匹配规则,通过将来自相应JavaFX组件的事件监听器方法与FXML属性进行匹配,来为FXML中的其他事件设置监听器。

FXML CSS样式

可以设置FXML文件中声明的JavaFX组件的样式。我们可以通过在FXML元素中嵌入一个style元素来实现。这是CSS样式化FXML文件中的JavaFX按钮的示例:

<?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文件中指定它。第二种方法是在用于加载FXML文档的FXMLLoader实例上设置控制器类的实例。此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.Hyman.javafx.MyFxmlController" >
    <Button text="Click me!"/ onAction="reactToClick()">
    </Button>
</VBox>

注意根元素(" VBox"元素)中的" fx:controller"属性。此属性包含控制器类的名称。加载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.Hyman.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>

这个例子将Button的onAction事件绑定到控制器类中的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");
    }

}

注意在buttonClicked方法上方的@FXML批注。此注释将方法标记为FXML绑定的目标。还要注意,在FXML文件中引用了名称" buttonClicked"。

从FXMLLoader获取控制器实例

一旦FXMLLoader实例加载了FXML文档,就可以通过FXMLLoader getController()方法获取对控制器实例的引用。这是一个例子:

MyFxmlController controllerRef = loader.getController();