系列中的上一篇
当前教程
JavaFX 属性
系列中的下一篇

系列中的上一篇: 效果、渐变和动画

系列中的下一篇: 使用 FXML

JavaFX 属性

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

 

介绍

应用于对象属性(而不是集合)的 JavaFX 属性监听器有两种类型:失效监听器和更改监听器。失效监听器在属性值不再有效时触发。对于此示例和后续示例,我们将讨论 MyShapesProperties 程序,该程序基于之前的 MyShapes 应用程序。在这个新程序中,我们在旋转的 StackPane 下方添加了第二个 Text 对象,并将其放置在 VBox 布局控件中。您可以在下面看到更新后的场景图,其中包含顶层的 VBox

MyShapesProperties scene graph  

失效监听器

失效监听器有一个单一方法,您可以使用 lambda 表达式覆盖该方法。让我们先向您展示非 lambda 表达式,这样您就可以看到完整的函数定义。当您单击 StackPane 时,鼠标单击处理程序会像以前一样旋转 StackPane 控件。第二个 Text 对象显示 RotateTransition 动画的状态,该动画由只读状态属性管理。您将看到 RUNNING、PAUSED 或 STOPPED。下图显示动画已暂停。

MyShapesProperties application with an invalidation listener

失效监听器包含一个可观察对象,允许您访问属性。由于可观察对象是非泛型的,您必须应用适当的类型转换才能访问属性值。以下是一种在附加到该属性的监听器中访问动画状态属性值的方法。请注意,我们使用属性 getter 方法 statusProperty() 附加监听器

rotate.statusProperty().addListener(new InvalidationListener() {
    @Override
    public  void invalidated(Observable observable) {
        text2.setText("Animation status: " +
        ((ObservableObjectValue<Animation.Status>)observable).getValue());
    }
});

在这里,我们使用 lambda 表达式实现相同的监听器

rotate.statusProperty().addListener(observable -> {
    text2.setText("Animation status: " +
    ((ObservableObjectValue<Animation.Status>)observable).getValue());
});

由于我们只访问状态属性值,我们可以使用返回枚举的方法 getStatus() 绕过可观察对象。这避免了强制转换表达式

rotate.statusProperty().addListener(observable -> {
    text2.setText("Animation status: " + rotate.getStatus());
});

 

更改监听器

当您需要访问可观察对象的先前值及其当前值时,请使用更改监听器。更改监听器提供可观察对象以及新旧值。更改监听器可能更昂贵,因为它们必须跟踪更多信息。以下是非 lambda 版本的更改监听器,它显示旧值和新值。请注意,您不必强制转换这些参数,因为更改监听器是泛型的

rotate.statusProperty().addListener(
        new ChangeListener<Animation.Status>() {
            @Override 
            public void changed(ObservableValue<? extends Animation.Status> observableValue,
                Animation.Status oldValue, Animation.Status newValue) {
                text2.setText("Was " + oldValue + ", Now " + newValue);
            }
});

以下是用更紧凑的 lambda 表达式编写的版本

rotate.statusProperty().addListener(
        (observableValue, oldValue, newValue) -> {
            text2.setText("Was " + oldValue + ", Now " + newValue);
});

您可以在下面看到 MyShapesProperties 运行,其中更改监听器附加到动画的状态属性。现在我们可以显示先前值和当前值。

MyShapesProperties application with a change listener  

绑定

JavaFX 绑定是一种灵活且功能丰富的机制,它允许您在许多情况下避免编写监听器。您可以使用绑定将 JavaFX 属性的值链接到一个或多个其他 JavaFX 属性。属性绑定可以是单向的或双向的。当属性类型相同时,单向 bind() 方法可能就足够了。但是,当属性类型不同或您想要根据多个属性计算值时,您将需要使用 fluent 和 bindings API。您还可以使用自定义绑定创建自己的绑定方法。  

单向绑定

最简单的绑定形式将一个属性的值链接到另一个属性的值。在这里,我们将 text2 的 rotate 属性绑定到 stackPane 的 rotate 属性

text2.rotateProperty().bind(stackPane.rotateProperty());

这意味着对 stackPane 旋转的任何更改都会立即更新 text2 的 rotate 属性。当此绑定在 MyShapesProperties 程序中设置时,StackPane 内部的任何点击都会启动为该节点定义的 rotate transition。这使得 StackPane 和 text2 组件一起旋转。 StackPane 旋转是因为我们启动了为该节点定义的 RotateTransition。text2 节点旋转是因为绑定表达式。

请注意,当您绑定属性时,除非先解除绑定,否则不能显式设置其值。  

双向绑定

双向绑定在两个属性之间提供双向关系。当一个属性更新时,另一个属性也会更新。以下是一个使用两个文本属性的示例

text2.textProperty().bindBidirectional(text.textProperty());

两个文本控件最初都显示“My Shapes”。当用户在 stackPane 内点击并 stackPane 旋转时,由于更改监听器,两个文本属性现在都将包含动画状态。

双向绑定并非完全对称;两个属性的初始值都将采用传递给 bindBidirectional() 调用的属性的值。与 bind() 不同,您可以在使用双向绑定时显式设置任一属性。  

Fluent API 和 Bindings API

fluent 和 bindings API 帮助您在多个属性需要参与绑定或需要执行某种计算或转换时构建绑定表达式。例如,以下绑定表达式显示 StackPane 的旋转角度,因为它从 0 度旋转到 360 度。文本属性是 String,而旋转属性是 double。绑定方法 asString() 将 double 转换为 String,并使用小数点后一位数字格式化数字

text2.textProperty().bind(stackPane.rotateProperty().asString("%.1f"));

对于更复杂的示例,让我们根据动画是否正在运行来更新 text2 的 stroke 属性(其颜色)。在这里,我们使用基于三元表达式的 When 构建绑定。这将 stroke 颜色设置为动画运行时的绿色,设置为动画停止或暂停时的红色

text2.strokeProperty().bind(new When(rotate.statusProperty()
        .isEqualTo(Animation.Status.RUNNING))
        .then(Color.GREEN).otherwise(Color.RED));

text2 文本属性在附加到我们之前显示的动画状态属性的更改监听器中设置。您可以在下面看到应用程序 MyShapesProperties,其中复杂的绑定表达式附加到 text2 strokeProperty。由于动画正在运行,因此 stroke 属性设置为 Color.GREEN

MyShapesProperties application with the fluent and bindings APIs


上次更新: 2023 年 9 月 12 日


系列中的上一篇
当前教程
JavaFX 属性
系列中的下一篇

系列中的上一篇: 效果、渐变和动画

系列中的下一篇: 使用 FXML