JavaFX自定义绑定不起作用。

4

我在一个较大规模的应用程序中遇到了这个问题,其中一些自定义绑定在源属性值更改时没有被更新。

我设法编写了一个简单的类来复制此问题,但我真的不明白为什么会发生这种情况。以下是一个快速测试,可复制该问题:

import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

public class TestingBindingsFx {

  private final ObjectProperty<MyEvent> objectProperty = new SimpleObjectProperty<MyEvent>(this, "objectProperty");
  private final BooleanProperty booleanProperty = new SimpleBooleanProperty(this, "booleanProperty");

  private ObjectBinding<MyEvent> bindingObj;
  private BooleanBinding bindingBool;

  public TestingBindingsFx(ObjectProperty<String> selection) {
    setupBindings(selection);
  }

  private void setupBindings(ObjectProperty<String> selection) {
    bindingObj = createObjectBinding(selection);
    bindingBool = createBooleanBinding(selection);
    objectProperty.bind(bindingObj);
    booleanProperty.bind(bindingBool);
  }

  private static ObjectBinding<MyEvent> createObjectBinding(ObjectProperty<String> selection) {
    return new ObjectBinding<MyEvent>() {
      {
        super.bind(selection);
      }

      @Override
      protected MyEvent computeValue() {
        System.out.println("createObjectBinding called");
        MyEvent ve = selection.get() == null ? MyEvent.EVENT1
            : MyEvent.EVENT2;
        return ve;
      }
    };
  }

  private static BooleanBinding createBooleanBinding(ObjectProperty<String> selection) {
    return new BooleanBinding() {
      {
        super.bind(selection);
      }

      @Override
      protected boolean computeValue() {
        System.out.println("createBooleanBinding called");
        return selection.get() == null ? true : false;
      }
    };
  }

  public static void main(String[] args) {
    ObjClass objclass = new ObjClass();
    System.out.println("Instantiating TestingBindingsFx...");
    TestingBindingsFx fx = new TestingBindingsFx(objclass.selection);

    objclass.selection.addListener(new ChangeListener<String>() {
      @Override
      public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {
        System.out.println("changed " + oldValue + "->" + newValue);
      }
    });

    System.out.println("Changing selection property values...");
    objclass.selection.set("Test 1");
    objclass.selection.set("Test 2");
  }

  enum MyEvent {
    EVENT1,
    EVENT2;
  }

  static class ObjClass {
    public final ObjectProperty<String> selection = new SimpleObjectProperty<String>(this, "selection");
  }
}

运行后,我看到:

Instantiating TestingBindingsFx...
createObjectBinding called
createBooleanBinding called
Changing selection property values...
changed null->Test 1
changed Test 1->Test 2

当我期望看到像这样的东西时:

Instantiating TestingBindingsFx...
createObjectBinding called
createBooleanBinding called
Changing selection property values...
changed null->Test 1
createObjectBinding called
createBooleanBinding called
changed Test 1->Test 2
createObjectBinding called
createBooleanBinding called
ChangeListener已经按照预期工作(只是为了验证目的而放置在那里),每当我更改选择属性的值时,它都会被调用。
但是自定义绑定在第一次后永远不会更新,查看代码后我无法理解原因。起初我认为这可能与弱引用有关,但我甚至将绑定对象转换为类级变量,仍然没有变化。
我感觉我可能错过了一些关键的东西, 但是在看了两个小时的代码之后,我就是找不到原因。
在我的实际应用程序中,更奇怪的是,其中一个自定义绑定实际上运行良好。
1个回答

7
这是因为JavaFX很懒,说真的。
如果一个依赖项无效,绑定将被标记为无效,并通知InvalidationListener可能会发生变化。除非您添加了ChangeListener或使用get检索值,否则computeValue方法永远不会被使用,因为“没有人想知道新值”。这可以提高性能。
使用InvalidationListener来绑定属性,并且这些属性也会被惰性刷新。
例如,您可以向属性添加更改侦听器,以强制在每次绑定无效时重新计算值:
private void setupBindings(ObjectProperty<String> selection) {
    bindingObj = createObjectBinding(selection);
    bindingBool = createBooleanBinding(selection);
    objectProperty.bind(bindingObj);
    booleanProperty.bind(bindingBool);

    objectProperty.addListener((a,b,c)-> {});
    booleanProperty.addListener((a,b,c)-> {});
}

太好了,就是这个!我知道我漏掉了一些简单的东西,我太专注于computeValue没有被调用,完全忘记了绑定的惰性特性。我不需要强制重新计算,惰性是完全可以的,我只是在改变源属性值后错过了读取目标属性的测试代码。感谢您的快速回答。 - mfc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接