以下问题是我的JavaFX应用程序中的一个错误的来源.奇怪的是,行为取决于特定的JavaFX属性是否附加了侦听器.当属性被ChangeListener观察时,一切都会正常工作,否则就不会.它开始把我逼疯了.

我设法将其分解为一个最小的代码示例.首先,我们需要一个公开不时更改的属性的类.在这里它被称为nameProperty().在本例中,我 Select 派生一个不断修改属性的单独线程,但在实际应用程序中,这是通过用户交互实现的.

class TestClass {
    private final SimpleObjectProperty<String> name = new SimpleObjectProperty<>();

    public TestClass() {
        new Thread(() -> {
            while(true) {
                try {
                    Thread.sleep(1000);
                    Platform.runLater(() -> name.set("A"));
                    Thread.sleep(1000);
                    Platform.runLater(() -> name.set("B"));
                }
                catch(InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

    public ReadOnlyObjectProperty<String> nameProperty() {
        return name;
    }
}

在Main方法中,在FX应用程序线程上,使用一个提取程序创建一个ObservableList<TestClass>,以便由ListChangeListener报告对列表元素的nameProperty()的更改.然后,我们创建测试类的一个实例,将其添加到列表中,并添加ListChangeListener以观察列表的更新.

public static void main(String[] args) throws Exception {
    Platform.startup(() -> {
        Callback<TestClass, Observable[]> extractor = obj -> new Observable[]{ obj.nameProperty() };
        ObservableList<TestClass> list = FXCollections.observableArrayList(extractor);

        TestClass test = new TestClass();
        list.add(test);

        list.addListener((ListChangeListener<TestClass>) c -> {
            while(c.next()) {
                if(c.wasUpdated()) {
                    System.out.println("List element was updated");
                }
            }
        });
    });

    Thread.sleep(60*60*1000);
}

我对输出的预期是,由于列表提取程序和不断修改的属性,它将如下所示:

List element was updated
List element was updated
List element was updated
List element was updated
List element was updated
...

但实际上,它看起来是这样的:

List element was updated
*silence*

现在奇怪的是,只要在代码中的任何位置向nameProperty()添加一个ChangeListener,例如

test.nameProperty().addListener(((observable, oldValue, newValue) -> {}));

它似乎就像预期的那样工作,并且列表不断地生成更改通知.

仅仅观察一个属性不应该改变与该属性绑定的其他东西的行为,对吗?但如果这是JavaFX中的一个错误,在我看来,这将是一个非常明显和根本的错误.所以,也许我确实搞砸了什么,尽管这个程序看起来很简单.顺便说一下,我在Windows10上使用的是OpenJFX版本21.

推荐答案

请注意,提取程序返回一个包含Observable的数组,如果我们查看documentation:

此类的实现应努力生成尽可能少的事件,以避免在事件处理程序中浪费太多时间.当第一个无效事件发生时,此库中的实现将其自身标记为无效.在重新计算它们的值并再次有效之前,它们不会再生成无效事件.

name属性从有效状态开始.您设置了该属性,它就失效了,激发了一个无效事件,最终列表更改侦听器收到了更新通知.但是,您永远不会查询name属性,这意味着它永远不会被验证.因此,尽管反复设置(设置不同的值),它仍然处于无效状态,并且不会触发任何更多的无效事件.如果您想继续收到无效事件的通知,则需要验证可观察值(S).

下面的示例显示了验证可观察对象和不验证可观察对象之间的区别:

import javafx.beans.Observable;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;


public class Main {

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

    static void doTestWithoutValidation() {
        StringProperty element = new SimpleStringProperty();

        ObservableList<Observable> list = FXCollections.observableArrayList(e -> new Observable[]{e});
        list.add(element);

        list.addListener((ListChangeListener.Change<? extends Observable> change) -> {
            while (change.next()) {
                if (change.wasUpdated()) {
                    System.out.println("List element updated");
                }
            }
        });

        System.out.println("Doing test WITHOUT validations...");

        element.set("Foo");
        element.set("Bar");
        element.set("Baz");

        System.out.println("DONE!");
        System.out.println();
    }

    static void doTestWithValidation() {
        StringProperty element = new SimpleStringProperty();

        ObservableList<Observable> list = FXCollections.observableArrayList(e -> new Observable[]{e});
        list.add(element);

        list.addListener((ListChangeListener.Change<? extends Observable> change) -> {
            while (change.next()) {
                if (change.wasUpdated()) {
                    System.out.println("List element updated");
                }
            }
        });

        System.out.println("Doing test WITH validations...");

        element.set("Foo");
        element.get(); // validate
        element.set("Bar");
        element.get(); // validate
        element.set("Baz");

        System.out.println("DONE!");
        System.out.println();
    }
}

输出:

Doing test WITHOUT validations...
List element updated
DONE!

Doing test WITH validations...  
List element updated
List element updated
List element updated
DONE!

您还必须小心垃圾收集.在您的代码中,在更新它以解决存在的并发问题之后,我相信在Runnable(传递给startup)返回之后,ObservableList本身有资格进行垃圾回收.

Java相关问答推荐

收听RDX中用户数据的变化

伪类focus-in不适用于PFA中的选项卡

Mongo DB Bson和Java:在子文档中添加和返回仅存在于父文档中的字段?

Bean定义不是从Spring ApplationConext.xml文件加载的

在Spring Boot应用程序中导致";MediaTypeNotSupportdException&qot;的映像上载

Spring和可编辑";where";@Query

使用While循环打印素数,无法正常工作

如何读取3个CSV文件并在控制台中按顺序显示?(Java)

使用SWIG将C++自定义单元类型转换为基本Java类型

在使用具有不同成本的谓词调用allMatch之前对Java流进行排序会带来什么好处吗?

Java 21中泛型的不兼容更改

如果执行@BeForeEach#repository.save(),则测试中的UnitTest最终UUID会发生更改

JXBrowser是否支持加载Chrome扩展?

Java 17与Java 8双重表示法

Android上的SQLite:Android.database.SQLite.SQLiteReadOnlyDatabaseException:try 写入只读数据库(代码1032 SQLite_readonly_DBMOVED)

JavaFX中ListView中的问题

如何从指定某些字段的父对象创建子对象

如何调查进程列表中不可见的活跃 MySQL 事务?

如何在 Android Studio 中删除 ImageView 和屏幕/父级边缘之间的额外空间?

如何解释泛型类层次 struct 中子类的返回值类型和参数定义?