使用 FXML
此页面由 Gail C. Anderson 和 Paul Anderson 贡献,根据 UPL 许可,摘自 使用 JavaFX 17 的现代 Java 客户端权威指南,由 Apress 友情提供。使用 FXML 声明场景图节点
您已经了解了 JavaFX API 如何创建场景图节点并为您配置它们。MyShapes
和 MyShapesProperties
程序仅使用 JavaFX 代码来构建和配置这些对象。另一种方法是使用 FXML 声明场景图节点,FXML 是一种基于 XML 的标记语言。FXML 允许您以声明式格式描述和配置场景图。这种方法有几个优点
- FXML 标记结构是分层的,因此它反映了场景图的结构。
- FXML 描述您的视图并支持模型-视图-控制器 (MVC) 架构,为大型应用程序提供更好的结构。
- FXML 减少了您必须编写的用于创建和配置场景图节点的 JavaFX 代码。
- 您可以使用 Scene Builder 设计 UI。此拖放工具是一个独立的应用程序,它提供场景的视觉渲染。Scene Builder 为您生成 FXML 标记。
- 您也可以使用文本和 IDE 编辑器编辑 FXML 标记。
FXML 会影响程序的结构。主应用程序类现在调用一个 FXMLLoader
。此加载器解析您的 FXML 标记,创建 JavaFX 对象,并将场景图插入根节点处的场景中。您可以拥有多个 FXML 文件,通常每个文件都有一个对应的 JavaFX 控制器类。此控制器类可能包含事件处理程序或其他动态更新场景的语句。控制器还包含管理特定视图的业务逻辑。
让我们回到我们的 MyShapes
示例(现在称为 MyShapesFXML
)并使用 FXML 文件作为视图和 CSS 进行样式设置。您可以在下面看到我们程序中的文件,这些文件已排列好,以便与构建工具或 IDE 一起使用。
JavaFX 源代码出现在 java 子目录下。resources 子目录包含 FXML 和 CSS 文件(此处为 Scene.fxml
和 Styles.css
)。
此程序包含一个旋转的 StackPane
、VBox
控件和第二个 Text
对象。Scene.fxml
描述了我们的场景图:一个顶层 VBox
,其中包含一个 StackPane
和 Text
元素。StackPane
包含 Ellipse
和 Text
形状。
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.effect.DropShadow?>
<?import javafx.scene.effect.Reflection?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.paint.LinearGradient?>
<?import javafx.scene.paint.Stop?>
<?import javafx.scene.shape.Ellipse?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<VBox alignment="CENTER" prefHeight="350.0" prefWidth="350.0" spacing="50.0"
xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx=http://javafx.com/fxml/1
fx:controller="org.modernclient.FXMLController">
<children>
<StackPane fx:id="stackPane" onMouseClicked="#handleMouseClick"
prefHeight="150.0" prefWidth="200.0">
<children>
<Ellipse radiusX="110.0" radiusY="70.0">
<fill>
<LinearGradient endX="0.5" endY="1.0" startX="0.5">
<stops>
<Stop color="DODGERBLUE" />
<Stop color="LIGHTBLUE" offset="0.5" />
<Stop color="LIGHTGREEN" offset="1.0" />
</stops>
</LinearGradient>
</fill>
<effect>
<DropShadow color="GREY" offsetX="5.0"
offsetY="5.0" />
</effect>
</Ellipse>
<Text text="My Shapes">
<font>
<Font name="Arial Bold" size="24.0" />
</font>
<effect>
<Reflection fraction="0.8" topOffset="1.0" />
</effect>
</Text>
</children>
</StackPane>
<Text fx:id="text2" text="Animation Status: ">
<font>
<Font name="Arial Bold" size="18.0" />
</font>
</Text>
</children>
</VBox>
顶层容器包含 JavaFX 控制器类的名称,其属性为 fx:controller。VBox
指定其对齐方式、首选大小和间距,然后是其子项:StackPane
和 Text
。在这里,我们使用首选大小配置 StackPane
。特殊属性 fx:id
指定与该节点相对应的变量名。在 JavaFX 控制器类中,您现在将看到此变量名用 @FXML
注释,用于 StackPane
。这就是您在控制器类中访问在 FXML 文件中声明的对象的方式。
此外,StackPane
指定了一个名为 #handleMouseClick
的 onMouseClicked
事件处理程序。此事件处理程序也在 JavaFX 控制器类中用 @FXML
注释。
在这里,StackPane
子项 Ellipse
和 Text
在 Children FXML 节点内声明。两者都没有关联的 fx:id
属性,因为控制器类不需要访问这些对象。您还会看到线性渐变、阴影和反射效果配置。
请注意,具有 fx:id text2
的 Text
对象出现在 StackPane
定义之后。这使得第二个 Text
对象出现在 VBox
中的 StackPane
下面。我们还指定了一个 fx:id
属性,以便从 JavaFX 控制器访问此节点。
控制器类
现在让我们向您展示控制器类。您会注意到代码更加紧凑,因为对象实例化和配置代码不再使用 Java 语句完成。所有这些现在都在 FXML 标记中指定。
package org.modernclient;
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.RotateTransition;
import javafx.beans.binding.When;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.util.Duration;
import java.net.URL;
import java.util.ResourceBundle;
public class FXMLController implements Initializable {
@FXML
private StackPane stackPane;
@FXML
private Text text2;
private RotateTransition rotate;
@Override
public void initialize(URL url, ResourceBundle rb) {
rotate = new RotateTransition(Duration.millis(2500), stackPane);
rotate.setToAngle(360);
rotate.setFromAngle(0);
rotate.setInterpolator(Interpolator.LINEAR);
rotate.statusProperty().addListener(
(observableValue, oldValue, newValue) -> {
text2.setText("Was " + oldValue + ", Now " + newValue);
});
text2.strokeProperty().bind(new When(rotate.statusProperty()
.isEqualTo(Animation.Status.RUNNING))
.then(Color.GREEN).otherwise(Color.RED));
}
@FXML
private void handleMouseClick(MouseEvent mouseEvent) {
if (rotate.getStatus().equals(Animation.Status.RUNNING)) {
rotate.pause();
} else {
rotate.play();
}
}
}
控制器类实现 Initializable
并覆盖方法 initialize()
,该方法在运行时为您调用。重要的是,私有类字段 stackPane
和 text2
用 @FXML
注释。@FXML
注释将控制器类中的变量名与 FXML 文件中描述的对象关联起来。控制器类中没有创建这些对象的代码,因为 FXMLLoader
会为您完成此操作。
initialize()
方法在这里执行三件事。首先,它创建并配置 RotateTransition
并将其应用于 stackPane
节点。其次,它向转换的状态属性添加一个更改侦听器。第三,text2
描边属性的绑定表达式根据旋转转换的状态指定其颜色。
带有 handleMouseClick()
的 @FXML
注释表示 FXML 文件配置了事件处理程序。此鼠标单击事件处理程序启动和停止旋转转换的动画。
JavaFX 应用程序类
主应用程序类 MyShapesFXML
现在变得非常简单。它的工作是调用 FXMLLoader
,它解析 FXML (Scene.fxml
),构建场景图并返回场景图根。您所要做的就是构建场景对象并像以前一样配置舞台,如下所示。
package org.modernclient;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class MyShapesFXML extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass()
.getResource("/fxml/Scene.fxml"));
Scene scene = new Scene(root, Color.LIGHTYELLOW);
scene.getStylesheets().add(getClass()
.getResource("/styles/Styles.css").toExternalForm());
stage.setTitle("MyShapesApp with JavaFX");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
添加 CSS
现在让我们向您展示如何使用 CSS 整合您自己的样式。JavaFX 的一个优点是它能够使用 CSS 对节点进行样式设置。JavaFX 附带一个默认样式表 Modena.css
。您可以增强这些默认样式或用新样式替换它们。我们在文件 Styles.css 中找到的示例 CSS 文件是一个单一样式类 (mytext
),它将字体样式设置为斜体
.mytext {
-fx-font-style: italic;
}
要使用此样式表,您必须首先加载该文件,无论是在应用程序的 start()
方法中还是在 FXML 文件中。将文件添加到可用样式表后,您可以将样式类应用于节点。例如,要将单独定义的样式类应用于特定节点,请使用
text2.getStyleClass().add("mytext");
这里,mytext
是样式类,text2
是程序中的第二个 Text
对象。或者,您可以在 FXML 文件中指定样式表。这种方法的优点是样式现在可以在 Scene Builder 中使用。以下是加载此自定义 CSS 文件并将自定义 CSS 样式类应用于 text2 Text
节点的修改后的 Scene.fxml
文件。
. . .
<VBox alignment="CENTER" prefHeight="350.0" prefWidth="350.0" spacing="50.0"
stylesheets="@../styles/Styles.css"
xmlns="http://javafx.com/javafx/10.0.1"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="org.modernclient.FXMLController">
<children>
<StackPane fx:id="stackPane" onMouseClicked="#handleMouseClick" prefHeight="150.0" prefWidth="200.0">
. . . code removed . . .
</StackPane>
<Text fx:id="text2" styleClass="mytext" text="Animation Status: ">
<font>
<Font name="Arial Bold" size="18.0" />
</font>
</Text>
</children>
</VBox>
使用 Scene Builder
Scene Builder 最初由 Oracle 开发,现在是开源的。您可以从 Gluon 下载它:https://gluonhq.com/products/scene-builder/。Scene Builder 是一个独立的拖放工具,用于创建 JavaFX UI。您可以在下面看到带有 MyShapesFXML
程序中 Scene.fxml
文件的主要 Scene Builder 窗口。
左上角的窗口显示了 JavaFX 组件库。此库包括容器、控件、形状、3D 等等。从这个窗口,您可以选择组件并将它们拖放到中间的视觉视图或左下角的 Document
窗口中。
Document
窗口显示场景图层次结构。您可以选择组件并在树中移动它们。右侧窗口是 Inspector
窗口,允许您配置每个组件,包括其属性、布局设置和代码。在此图中,StackPane
在 Document
层次结构窗口中被选中,并出现在中心视觉视图中。在 Inspector
窗口中,OnMouseClicked
属性设置为 #handleMouseClick
,这是 JavaFX 控制器类中对应方法的名称。
Scene Builder 在构建真实的基于表单的 UI 时特别有用。您可以可视化场景层次结构并轻松配置布局和对齐设置。
最后更新: 2023 年 9 月 12 日