当前教程
JavaFX 应用程序基本结构示例
系列中的下一个

系列中的下一个: JavaFX 布局控件

JavaFX 应用程序基本结构示例

此页面由 Gail C. AndersonPaul AndersonUPL 下贡献,摘自 使用 JavaFX 17 的现代 Java 客户端权威指南,由 Apress 慷慨提供。

 

JavaFX 阶段和场景图

JavaFX 应用程序由 JavaFX 平台控制,JavaFX 平台是一个运行时系统,它构建您的应用程序对象并构建JavaFX 应用程序线程。要构建 JavaFX 应用程序,您必须扩展JavaFX 应用程序类。JavaFX 运行时系统控制应用程序生命周期并调用应用程序start()方法。

JavaFX 使用一个戏剧隐喻:顶级容器是Stage,由平台为您构建。在桌面应用程序中,Stage 是窗口。它的外观取决于主机系统,在 Mac OS X、Windows 和 Linux 平台上有所不同。通常,窗口装饰有用于调整大小、最小化和退出应用程序的控件。也可以构建无装饰窗口。您也可以为其他环境专门化应用程序类。例如,使用Gluon Mobile Application框架,您的程序扩展了 Mobile Application,这是一个专门为移动设备编写的应用程序类。 

JavaFX 是单线程的

您必须始终在JavaFX 应用程序线程上构建和修改Stage 及其场景对象。请注意,JavaFX(如Swing)是一个单线程 UI 模型。对于 JavaFX 开发人员来说,这主要是一个直接的限制。当您创建 UI 元素、响应事件处理程序、使用动画管理动态内容或在场景图中进行更改时,工作将继续在 JavaFX 应用程序线程上执行。

但是,为了保持 UI 的响应能力,您应该将长时间运行的工作分配给单独线程中的后台任务。在这种情况下,修改 UI 的工作必须与在后台线程上执行的工作分开。幸运的是,JavaFX 拥有一个完善的并发 API,可以帮助开发人员将长时间运行的任务分配给一个或多个单独的线程。这使 UI 线程能够对用户事件做出响应。 

分层节点结构

继续使用戏剧隐喻,Stage 持有一个场景。场景由 JavaFX 元素组成,例如根元素,它是顶层场景元素,包含所谓的场景图。

场景图是元素的严格分层结构,用于可视化您的应用程序。这些元素称为节点。节点只有一个父节点(根节点除外),并且可能包含其他节点。或者,节点可以是没有任何子节点的叶节点。节点必须添加到场景图中才能参与该场景的渲染。此外,节点只能添加到一个场景中,除非它首先被移除,然后添加到其他地方。

通常,父节点通过根据布局规则和您配置的任何约束在场景中排列子节点来管理子节点。JavaFX 使用二维坐标系进行二维图形,原点位于场景的左上角,如下图所示。x 轴上的坐标值向右增加,y 轴上的坐标值向下增加。

JavaFX 2D coordinate system

JavaFX 还支持 3D 图形,并使用 z 轴值表示第三维,提供深度。JavaFX 具有绝对坐标系,以及相对于父节点的局部坐标系。在每种情况下,坐标系的原点都是父节点的左上角。通常,布局控件隐藏了组件在场景中放置的复杂性,并为您管理其子节点的放置。组件放置基于特定的布局控件以及您如何配置它。也可以嵌套布局控件。例如,您可以在HBox 中放置多个 VBox 控件,或者将AnchorPane 放入SplitPane 控件的一个窗格中。其他父节点是更复杂的视觉节点,例如TextFieldTextAreaButton。这些节点具有管理的子部分。例如,Button 包括一个带标签的文本部分和可选图形。此图形可以是任何节点类型,但通常是图像或图标。

请记住,叶节点没有子节点。示例包括Shape(例如RectangleEllipseLinePathText)以及ImageView,用于渲染图像的节点。

提醒一下:您应该使用纯文本编辑器创建和保存此文件。使用文字处理器将不起作用。 

一个简单的形状示例

下图显示了一个名为MyShapes 的简单 JavaFX 应用程序,它在应用程序窗口中显示一个椭圆形和一个居中的文本元素。此窗口的外观因底层平台而异。当您调整窗口大小时,可见元素将保持在调整大小的空间中居中。即使这是一个简单的程序,这里也有很多关于 JavaFX 渲染、布局功能和节点的知识。

MyShapes application

此应用程序的源代码位于MyShapes 程序中。类MyShapes 是主类,扩展了Application。JavaFX 运行时系统实例化MyShapes 以及主 Stage,它将其传递给重写的start()方法。运行时系统为您调用start()方法。

package org.modernclient;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MyShapes extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        // Create an Ellipse and set fill color
        Ellipse ellipse = new Ellipse(110, 70);
        ellipse.setFill(Color.LIGHTBLUE);
        // Create a Text shape with font and size
        Text text = new Text("My Shapes");
        text.setFont(new Font("Arial Bold", 24));
        StackPane stackPane = new StackPane();
        stackPane.getChildren().addAll(ellipse, text);
        Scene scene = new Scene(stackPane, 350, 230,
                       Color.LIGHTYELLOW);
        stage.setTitle("MyShapes with JavaFX");
        stage.setScene(scene);
        stage.show();
    }
    public static void main(String[] args) {
        launch(args);
    }
}

请注意引用javafx.applicationjavafx.scenejavafx.stage 中的包的导入语句。


注意:请确保为任何导入语句指定正确的包。一些 JavaFX 类(例如 Rectangle)与其 AWT 或 Swing 对应类具有相同的类名。所有 JavaFX 类都是包javafx 的一部分。


此程序创建了几个节点,并将它们添加到StackPane 布局容器中。该程序还创建了场景,配置了舞台,并显示了舞台。让我们详细了解这些步骤。

首先,我们创建一个Ellipse 形状,以像素为单位提供宽度和高度。由于Ellipse 扩展了Shape,我们也可以配置任何Shape 属性。这包括填充,它允许您指定内部油漆值。 

颜色

Shape 的填充属性可以是 JavaFX 颜色、线性渐变、径向渐变或图像。让我们简要讨论一下颜色。您可以在 JavaFX 中通过多种方式指定颜色。在这里,我们将Ellipse 的填充属性设置为Color.LIGHTBLUE

// Create an Ellipse and set fill color
Ellipse ellipse = new Ellipse(110, 70);
ellipse.setFill(Color.LIGHTBLUE);

目前,JavaFX Color 类中有 147 种预定义颜色,按字母顺序从ALICEBLUEYELLOWGREEN 排列。但是,您也可以使用十六进制表示法或十进制数使用 Web RGB 值指定Color。您可以选择提供一个 alpha 值来表示透明度。完全不透明为 1,完全透明为 0。例如,透明度为 .5 会显示颜色,但也会让背景颜色透出来。以下是一些使用Color 设置形状填充的示例

ellipse.setFill(Color.LIGHTBLUE);              // Light blue, fully opaque
ellipse.setFill(Color.web("#ADD8E6"));         // Light blue, fully opaque
ellipse.setFill(Color.web("#ADD8E680"));       // Light blue, .5 opaque
ellipse.setFill(Color.web("0xADD8E6"));        // Light blue, fully opaque
ellipse.setFill(Color.web("0xADD8E680"));      // Light blue, .5 opaque
ellipse.setFill(Color.rgb(173, 216, 230));     // Light blue, fully opaque
ellipse.setFill(Color.rgb(173, 216, 230, .5)); // Light blue, .5 opaque

值得注意的是,您可以对颜色的值进行插值,这就是 JavaFX 如何构建渐变的方式。我们将在稍后向您展示如何创建线性渐变。 

文本

接下来,我们创建一个 Text 对象。Text 也是一个带有附加属性的 Shape,例如字体、文本对齐方式、文本和换行宽度。构造函数提供文本,setFont() 方法设置其字体。

// Create a Text shape with font and size
Text text = new Text("My Shapes");
text.setFont(new Font("Arial Bold", 24));

 

JavaFX 坐标系

请注意,我们创建了椭圆和文本节点,但它们尚未在我们的场景图中。在将它们添加到场景之前,我们必须将这些节点放入某种布局容器中。布局控件在管理场景图中非常重要。这些控件不仅为您排列组件,而且还响应事件,例如调整大小、添加或删除元素,以及场景图中一个或多个节点大小的任何更改。

为了向您展示布局控件的重要性,让我们用一个 Group 替换原始示例中的 StackPane,并手动指定放置位置。 Group 是一个父节点,它管理其子节点,但不提供任何布局功能。在这里,我们创建一个组,并使用构造函数添加椭圆和文本元素。然后,我们将组指定为场景的根节点

Group group = new Group(ellipse, text);
...
Scene scene = new Scene(group, 350, 230, Color.LIGHTYELLOW);

Group 对其子节点使用默认对齐设置,并将所有内容放置在原点 (0,0) 处,即场景的左上角。对于 Text,默认放置位置是文本元素的左下角。在这种情况下,唯一可见的部分将是延伸到下边缘的字母(MyShapes 的小写 yp 字母)。椭圆将以组原点 (0,0) 为中心,因此只有右下象限可见。这种安排显然不是我们想要的。为了解决这个问题,让我们手动将形状居中在 350 × 230 场景中,如下所示

Group group = new Group(ellipse, text);
// Manually placing components is tedious and error-prone
ellipse.setCenterX(175);
ellipse.setCenterY(115);
text.setX(175-(text.getLayoutBounds().getWidth()/2));
text.setY(115+(text.getLayoutBounds().getHeight()/2));
. . .
Scene scene = new Scene(group, 350, 230, Color.LIGHTYELLOW);

现在,形状将很好地居中在场景中。但这仍然不是理想的。当窗口调整大小(除非您编写检测并对窗口调整大小做出反应的代码)时,形状将保持在场景中的这些坐标处。而且您不想那样做。相反,请使用 JavaFX 布局控件!


上次更新: 2023 年 9 月 11 日


当前教程
JavaFX 应用程序基本结构示例
系列中的下一个

系列中的下一个: JavaFX 布局控件