示例 FXML

一个简单的 FXML 文档,概述了包含按钮和标签节点的 AnchorPane

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" 
        fx:controller="com.example.FXMLDocumentController">
    <children>
        <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
        <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
    </children>
</AnchorPane>

此示例 FXML 文件与控制器类相关联。在这种情况下,FXML 和控制器类之间的关联是通过在 FXML 的根元素中指定类名作为 fx:controller 属性的值来实现的:fx:controller="com.example.FXMLDocumentController"。控制器类允许执行 Java 代码以响应对 FXML 文件中定义的 UI 元素的用户操作:

package com.example ;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class FXMLDocumentController {
    
    @FXML
    private Label label;
    
    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("You clicked me!");
        label.setText("Hello World!");
    }
    
    @Override
    public void initialize(URL url, ResourceBundle resources) {
        // Initialization code can go here. 
        // The parameters url and resources can be omitted if they are not needed
    }    
    
}

FXMLLoader 可用于加载 FXML 文件:

public class MyApp extends Application {

    @Override
    public void start(Stage stage) throws Exception {

        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("FXMLDocument.fxml"));
        Parent root = loader.load();
        
        Scene scene = new Scene(root);
        
        stage.setScene(scene);
        stage.show();
    }

}

load 方法执行多个操作,了解它们发生的顺序很有用。在这个简单的例子中:

  1. FXMLLoader 读取并解析 FXML 文件。它创建与文件中定义的元素对应的对象,并记下在其上定义的任何 fx:id 属性。

  2. 由于 FXML 文件的根元素定义了 fx:controller 属性,因此 FXMLLoader 创建了它指定的类的新实例。默认情况下,这是通过在指定的类上调用无参构造函数来实现的。

  3. 定义了 fx:id 属性的任何元素,其中控制器中的字段具有匹配的字段名称,并且 public(不推荐)或带注释的 @FXML(推荐)被注入到那些相应的字段中。所以在这个例子中,因为 FXML 文件中有一个 Label,其中 fx:id="label" 和控制器中的一个字段定义为

    @FXML
    private Label label ;
    

    label 字段使用 FXMLLoader 创建的 Label 实例进行初始化。

  4. 事件处理程序在 FXML 文件中的任何元素中注册,并定义了 onXXX="#..." 属性。这些事件处理程序调用控制器类中的指定方法。在这个例子中,由于 Button 具有 onAction="#handleButtonAction",并且控制器定义了一种方法

    @FXML
    private void handleButtonAction(ActionEvent event) { ... }
    

    当对按钮触发动作时(例如,用户按下它),将调用此方法。该方法必须具有 void 返回类型,并且可以定义与事件类型匹配的参数(在此示例中为 ActionEvent),或者可以不定义任何参数。

  5. 最后,如果控制器类定义了 initialize 方法,则调用此方法。请注意,这是在注入 @FXML 字段后发生的,因此可以在此方法中安全地访问它们,并使用与 FXML 文件中的元素对应的实例进行初始化。initialize() 方法既可以不参数,也可以采用 URLResourceBundle。在后一种情况下,这些参数将由表示 FXML 文件位置的 URL 以及通过 loader.setResources(...)FXMLLoader 上设置的任何 ResourceBundle 填充。如果没有设置,这些中的任何一个都可以是 null