【问题标题】:JavaFX DataBinding behaves strangeJavaFX DataBinding 行为奇怪
【发布时间】:2016-03-09 07:57:23
【问题描述】:

我有一个填写结果列表的 Worker。 ListView 绑定到此结果列表,并在部分结果可用时立即填充。效果很好!

然后有一个按钮可以处理列表中的项目。因此,只要列表为空,就应该禁用它。这适用于所有其他列表。但不适合这个。

我通过剥离 UI 组件并仅使用属性来减少示例。结果是一样的。要么存在某种错误,要么我不明白绑定是如何工作的。

我希望在这方面有任何帮助。

只需将此代码放入 main() 方法中,您就会看到问题所在。我正在使用 Java 1.8 更新 74。

// a list of items, e.g. in a list view
ReadOnlyObjectWrapper<ObservableList<String>> items = new ReadOnlyObjectWrapper<>(
            FXCollections.observableArrayList(new ArrayList<>()));

// a button that is disabled if there are no items
BooleanProperty disabled = new SimpleBooleanProperty();
disabled.bind(Bindings.createBooleanBinding(() -> Boolean.valueOf(items.get().isEmpty()), items));

// a list with results, e.g. from a worker
ReadOnlyObjectWrapper<ObservableList<String>> results = new ReadOnlyObjectWrapper<>(
            FXCollections.observableArrayList(new ArrayList<>()));
// the items are bound to the results (the list is showing partial result then)
items.bind(results);

// the button is still disabled
System.out.println(disabled.get()); // expected true !WORKS!

// add a result (should inform the items and the button)
results.get().add("Hello");

// both list should be identical now
System.out.println(results.get()); // expected [Hello] !WORKS!
System.out.println(items.get()); // expected [Hello] !WORKS!

// the button should not be disabled anymore
System.out.println(disabled.get()); // expected false !FALIED!

// try to re-bind the button
disabled.unbind();
disabled.bind(Bindings.createBooleanBinding(() -> Boolean.valueOf(items.get().isEmpty()), items));
System.out.println(disabled.get()); // expected false !WORKS!

【问题讨论】:

    标签: java data-binding javafx


    【解决方案1】:

    绑定是为items 完成的,并且只为items 完成。这意味着仅当您替换列表时该值才会更新,而仅在您修改它时不会更新。

    要在items包裹的列表发生变化时得到通知,每次替换列表时都需要将属性绑定到列表的“空性条件”:

    items.addListener((observable, oldValue, newValue)-> {
        if (newValue == null) {
             disabled.unbind();
             disabled.set(true);
        } else {
             disabled.bind(Bindings.isEmpty(newValue));
        }
    });
    

    或者更简单 - 使用ListProperty:

    ListProperty<String> items = new SimpleListProperty<>();
    disabled.bind(Bindings.isEmpty(items));
    

    Bindings.createXYZBinding(callable, o1, 02, ..., oN) 创建一个XYZBinding,它缓存callable 在评估时返回的值。评估是懒惰地发生的,即当绑定是脏的并且:

    1. 在绑定对象上调用get
    2. 第一个监听器被添加到绑定或
    3. 至少添加了一个 ChangeListener 并且绑定变脏了

    绑定是脏的,如果

    1. 该值尚未评估(初始状态)
    2. 在创建绑定时添加到o1、...、oN 的侦听器之一接收到更改事件。

    如果评估了callback,则绑定再次变得干净(即不脏)。

    绑定缓存callback 评估的结果,并在调用get 或需要将数据传递给ChangeListener 时使用此结果。只有在评估后缓存的值发生变化时才会调用侦听器。

    【讨论】:

    • 我根据您的信息做了更多的测试。实际上,在我的代码中对items.bind(results); 的调用会更改由items 包装的值。如果您前后调用items.get(),您将获得不同的值。所以任何绑定到由items 包裹的列表是没有用的。如果我之后再做,一切正常。我没想到这一点,仍然不完全明白为什么。
    • @Future:我在答案中添加了几行。我希望他们能帮助你理解并且不要让你感到困惑......基本上绑定不会重新计算值,除非他们根据依赖项上发生的事件认为它是必要的,并且Propertys 不会将侦听器添加到包装的值,除非它是特殊属性,例如 MapPropertyListPropertySetProperty
    猜你喜欢
    • 2012-09-01
    • 2014-11-26
    • 2015-09-17
    • 2022-01-15
    • 2016-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-21
    相关资源
    最近更新 更多