基于 Bindings.size() 的 JavaFX 绑定未被触发

JavaFX Bindings based on Bindings.size() is not getting triggered

是的,这个问题和几年前问的很相似。事实上,我的示例代码也是基于那个代码的。我也尝试了那里给出的答案,但没有用。

示例代码如下:

package com.example.bindingssize;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.IntegerBinding;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class HelloApplication extends Application {

    private IntegerBinding sizeBinding;
    private BooleanBinding isEven;

    @Override
    public void start(Stage primaryStage) {
        final ObservableList<String> list = FXCollections.observableArrayList();
        primaryStage.setTitle("Demonstrator");

        // Button
        Button addButton = new Button();
        addButton.setText("Add Element");
        addButton.setOnAction(event -> list.add("TEST"));

        // ListView
        ListView<String> lv = new ListView<>();
        lv.setItems(list);

        // Add elements to root
        VBox root = new VBox();
        root.getChildren().add(addButton);
        root.getChildren().add(lv);

        // Show scene
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();

        sizeBinding = Bindings.size(list);

        isEven = Bindings.createBooleanBinding(() -> {
            System.out.println(sizeBinding.get()); // key line 1 of 2
            System.out.println("inside isEven");
            return list.size() % 2 == 0;
        }, sizeBinding);

        // key line 2 of 2
        isEven.addListener((obs, o, n) -> System.out.println(isEven.get() ? "Even" : "Odd"));
    }

    public static void main(String[] args) {
        launch();
    }
}

以上代码有效。每次单击添加按钮时,'isEven' 中的打印语句都会按预期打印。但是,此行为完全取决于 2 个关键行。如果缺少一个或两个关键行,打印语句将不会打印任何内容。在这里,我将所有内容都设为 class 的字段,因此它们不应该被垃圾收集。那么这种行为的原因是什么?

这是 JavaFX 11.0.2,AdoptOpenJDK-11.0.11+9,在 macOS Catalina 上。

绑定是惰性的

在 JavaFX 中,绑定具有“有效”状态。当绑定处于有效状态时值可能发生更改,则绑定会触发失效事件。但是,如果绑定当前无效,则不会触发任何失效事件。 “验证”绑定的方法是查询它的值。此设置允许绑定仅在需要该值时计算其值。

取决于绑定

您的 isEven 绑定依赖于 sizeBinding 绑定。这是通过 isEven 绑定通过 InvalidationListener 观察 sizeBinding 绑定来实现的。如上所述,仅当绑定从有效状态变为无效状态时才会触发失效事件。

问题是,除了你的 println(sizeBinding.get()) 调用,你从不查询 sizeBinding 的值。这意味着您永远不会验证绑定,这意味着 isEven 绑定永远不知道要重新计算它的值。如果你有:

isEven = Bindings.createBooleanBinding(() -> sizeBinding.get() % 2 == 0, sizeBinding);

那就可以了。请注意,它查询 sizeBinding 值。

更改监听器

如果将 ChangeListener 添加到绑定中,则会立即计算该值。原因很简单:监听器同时传递了旧的 和新的 值。如果没有将 ChangeListener 添加到 isEven 绑定,绑定将保持惰性。而且由于您从不验证 isEven 绑定,因此它永远不需要(重新)计算其值。

但是,添加 ChangeListener 后,会急切地(重新)计算该值。但请注意 isEven 绑定只有在知道值已更改时才会重新计算其值。换句话说,您仍然必须确保 sizeBinding 绑定在适当的时候得到验证。