以下问题是我的 JavaFX 应用程序中错误的根源。奇怪的是,该行为取决于特定的 JavaFX 属性是否附加了侦听器。当属性被 a 观察时一切正常ChangeListener,否则无效。它开始让我发疯......
我设法将其分解为一个最小的代码示例。首先,我们需要一个类来公开一个时不时变化的属性。这里就叫nameProperty(). 在此示例中,我选择生成一个单独的线程来不断修改属性,但在实际应用程序中,它是通过用户交互发生的。
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;
}
}
Run Code Online (Sandbox Code Playgroud)
在 main 方法中,在 FX 应用程序线程上,ObservableList<TestClass>使用提取器创建 an ,以便nameProperty()由 报告对列表元素的更改ListChangeListener。然后,我们创建测试类的一个实例,将其添加到列表中,并添加 aListChangeListener以观察列表的更新。
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);
}
Run Code Online (Sandbox Code Playgroud)
我对输出的期望是,由于列表提取器和属性不断修改,它看起来像这样:
List element was updated
List element was updated
List element was updated
List element was updated
List element was updated
...
Run Code Online (Sandbox Code Playgroud)
但它看起来像这样:
List element was updated
*silence*
Run Code Online (Sandbox Code Playgroud)
现在奇怪的部分是,一旦ChangeListener将 a 添加到nameProperty()代码中的任何位置,例如
test.nameProperty().addListener(((observable, oldValue, newValue) -> {}));
Run Code Online (Sandbox Code Playgroud)
它似乎按预期工作,并且列表不断生成更改通知。
仅观察一个属性不应该改变与该属性绑定的其他事物的行为,对吧?但如果这是 JavaFX 中的一个错误,那么在我看来,这将是一个非常明显且根本的错误。所以也许我确实搞砸了一些事情,尽管程序看起来很简单。顺便说一下,我在 Windows 10 上使用 OpenJFX 版本 21。
请注意,提取器返回一个数组Observable,如果我们查看文档:
此类的实现应努力生成尽可能少的事件,以避免在事件处理程序中浪费太多时间。当第一个失效事件发生时,该库中的实现将自身标记为无效。在重新计算其值并再次有效之前,它们不会再生成失效事件。
该name属性一开始处于有效状态。您设置该属性,它就会失效,触发失效事件,最终列表更改侦听器会收到更新通知。但是您永远不会查询该name属性,这意味着它永远不会被验证。因此,尽管它被一遍又一遍地设置(设置为不同的值),但它仍然处于无效状态,并且不会触发任何更多的无效事件。如果您想继续收到失效事件的通知,您需要验证可观察量。
下面的示例显示了验证可观察值与不验证可观察值之间的区别:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
Doing test WITHOUT validations...
List element updated
DONE!
Doing test WITH validations...
List element updated
List element updated
List element updated
DONE!
Run Code Online (Sandbox Code Playgroud)
您还必须小心垃圾收集。在您的代码中,在更新它以解决存在的并发问题后,我相信在(传递给)返回ObservableList后,它本身可能有资格进行垃圾收集。Runnablestartup
| 归档时间: |
|
| 查看次数: |
117 次 |
| 最近记录: |