在 JavaFX 中绑定对象
Binding objects in JavaFX
我很困惑如何使用对象绑定将 ObjectProperty 绑定到另一个对象。根据我在 javaFX 文档中阅读和理解的内容,这个 MCVE 应该输出 20,但它不起作用。输出即将变为 0。
// I tried to bind the attribute B's Integer attribute in class A to class C's SimpleIntegerProperty
package sample;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
class ObjectBindingImpl extends ObjectBinding {
private A a;
public ObjectBindingImpl(A a) {
this.a = a;
bind(new SimpleObjectProperty<>(a.b));
}
@Override
protected Object computeValue() {
return a.b == null ? null : a.b.b;
}
}
public class Main {
public static void main(String[] args) {
A a = new A();
C c = new C();
c.c.bind(new ObjectBindingImpl(a));
a.b = new B(20); // Clearly I change the value to 20 here
System.out.println(c.c.get()); // Expected output 20 as B.b = 20 but output returned is 10.
}
}
class A {
B b = new B(10); // Assigned it value 10.
}
class B {
Integer b;
B(int b) {
this.b = b;
}
}
class C {
SimpleIntegerProperty c = new SimpleIntegerProperty();
}
根据我的理解,因为我调用了 bind(new SimpleObjectProperty(a.b)) 所以每次 a.b 的值发生变化时,绑定都会失效,然后稍后,它会使用computeValue() 方法 impl 为其分配更新的值。
但似乎无论分配给它的第一个值是什么,即使在更改数据后也不会改变。我在这里错过了什么?我怎样才能让这个例子起作用?
您的代码中的问题是,您没有更改 属性,而只更改了基础数据。为了使 属性-Bindings 识别您的更改,您需要设置 属性:
A a = new A();
SimpleObjectProperty aProperty = new SimpleObjectProperty<>(a);
SimpleObjectProperty bProperty = new SimpleObjectProperty<>(a.b);
System.out.println(aProperty.get()); // A, because we set it to a.
aProperty.bind(bProperty);
System.out.println(aProperty.get()); // null, because a.b is null (and we bound the value from bProperty now)
bProperty.set(new B()); // Set the bProperty. This will update the aProperty as well, because it is bound to bProperty
System.out.println(aProperty.get());
// B, because we set bProperty to B, and as aProperty is bound to bProperty, this will change aProperty to it as well.
这也在所有 JavaFX 控件中完成。看看例如TableView
:
private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final ObjectProperty<ObservableList<S>> itemsProperty() { return items; }
public final void setItems(ObservableList<S> value) { itemsProperty().set(value); }
public final ObservableList<S> getItems() {return items.get(); }
所以每个 setter 都会设置 属性。然后,您可以将 属性 绑定到另一个(或添加一个带有 addListener
的侦听器以对更改做出反应)。
我的解释或多或少类似于@Maran23。您可以使绑定仅在可观察属性之间起作用。
在您提供的示例中,绑定在 C.c 和绑定 class 中的“新 SimpleObjectProperty”实例之间。
您正在更新 a.b 值,该值不是可观察的 属性。
要获得反映在 C.c 中的值,应更新“new SimpleObjectProperty”中的值。这对于您的示例是不可能的,因为该实例是动态创建的,没有更新它的参考。
因此,为了让您的示例正常工作,请为您在 ObjectBindingImpl 中创建的可观察对象 属性 创建一个引用,并确保更新此可观察对象的 属性 值。
我还想纠正你关于
的一个假设
"...然后稍后,它使用 computeValue() 方法 impl 为其分配更新值"
它不会在某个随机时间更新。只有当您显式调用可观察对象的“get”方法时,它才会调用 computeValue 属性。在调用 getter 之前,可观察对象 属性 将处于“无效”状态。
请在下面找到演示,其中包含我提到的更改。另请注意,还有许多其他有效的方法可以让绑定发生,但我正在尝试用您提供的示例回答您的实际 OP。
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
public class ObjectBinding_Demo {
public static void main(String[] args) {
new ObjectBinding_Demo().init();
}
private void init() {
A a = new A();
C c = new C();
ObjectBindingImpl binding = new ObjectBindingImpl(a);
c.c.bind(binding);
a.b = new B(20);
binding.obj.setValue(a.b); // Update the observable property with the new value
System.out.println("Value set in binding observable property..");
System.out.println(c.c); // Output:: IntegerProperty [bound, invalid]
System.out.println(c.c.get()); // Output:: 'Computing value...' string and then 20
System.out.println(c.c); // Output:: IntegerProperty [bound, value: 20]
}
class ObjectBindingImpl extends ObjectBinding {
private A a;
ObjectProperty obj; // Observable property reference for future updating.
public ObjectBindingImpl(A a) {
this.a = a;
obj = new SimpleObjectProperty(a.b);
bind(obj);
}
@Override
protected Object computeValue() {
System.out.println("Computing value...");
return a.b == null ? null : a.b.b;
}
}
class A {
B b = new B(10); // Assigned it value 10.
}
class B {
Integer b;
B(int b) {
this.b = b;
}
}
class C {
SimpleIntegerProperty c = new SimpleIntegerProperty();
}
}
我很困惑如何使用对象绑定将 ObjectProperty 绑定到另一个对象。根据我在 javaFX 文档中阅读和理解的内容,这个 MCVE 应该输出 20,但它不起作用。输出即将变为 0。
// I tried to bind the attribute B's Integer attribute in class A to class C's SimpleIntegerProperty
package sample;
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
class ObjectBindingImpl extends ObjectBinding {
private A a;
public ObjectBindingImpl(A a) {
this.a = a;
bind(new SimpleObjectProperty<>(a.b));
}
@Override
protected Object computeValue() {
return a.b == null ? null : a.b.b;
}
}
public class Main {
public static void main(String[] args) {
A a = new A();
C c = new C();
c.c.bind(new ObjectBindingImpl(a));
a.b = new B(20); // Clearly I change the value to 20 here
System.out.println(c.c.get()); // Expected output 20 as B.b = 20 but output returned is 10.
}
}
class A {
B b = new B(10); // Assigned it value 10.
}
class B {
Integer b;
B(int b) {
this.b = b;
}
}
class C {
SimpleIntegerProperty c = new SimpleIntegerProperty();
}
根据我的理解,因为我调用了 bind(new SimpleObjectProperty(a.b)) 所以每次 a.b 的值发生变化时,绑定都会失效,然后稍后,它会使用computeValue() 方法 impl 为其分配更新的值。
但似乎无论分配给它的第一个值是什么,即使在更改数据后也不会改变。我在这里错过了什么?我怎样才能让这个例子起作用?
您的代码中的问题是,您没有更改 属性,而只更改了基础数据。为了使 属性-Bindings 识别您的更改,您需要设置 属性:
A a = new A();
SimpleObjectProperty aProperty = new SimpleObjectProperty<>(a);
SimpleObjectProperty bProperty = new SimpleObjectProperty<>(a.b);
System.out.println(aProperty.get()); // A, because we set it to a.
aProperty.bind(bProperty);
System.out.println(aProperty.get()); // null, because a.b is null (and we bound the value from bProperty now)
bProperty.set(new B()); // Set the bProperty. This will update the aProperty as well, because it is bound to bProperty
System.out.println(aProperty.get());
// B, because we set bProperty to B, and as aProperty is bound to bProperty, this will change aProperty to it as well.
这也在所有 JavaFX 控件中完成。看看例如TableView
:
private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final ObjectProperty<ObservableList<S>> itemsProperty() { return items; }
public final void setItems(ObservableList<S> value) { itemsProperty().set(value); }
public final ObservableList<S> getItems() {return items.get(); }
所以每个 setter 都会设置 属性。然后,您可以将 属性 绑定到另一个(或添加一个带有 addListener
的侦听器以对更改做出反应)。
我的解释或多或少类似于@Maran23。您可以使绑定仅在可观察属性之间起作用。
在您提供的示例中,绑定在 C.c 和绑定 class 中的“新 SimpleObjectProperty”实例之间。
您正在更新 a.b 值,该值不是可观察的 属性。
要获得反映在 C.c 中的值,应更新“new SimpleObjectProperty”中的值。这对于您的示例是不可能的,因为该实例是动态创建的,没有更新它的参考。
因此,为了让您的示例正常工作,请为您在 ObjectBindingImpl 中创建的可观察对象 属性 创建一个引用,并确保更新此可观察对象的 属性 值。
我还想纠正你关于
的一个假设"...然后稍后,它使用 computeValue() 方法 impl 为其分配更新值"
它不会在某个随机时间更新。只有当您显式调用可观察对象的“get”方法时,它才会调用 computeValue 属性。在调用 getter 之前,可观察对象 属性 将处于“无效”状态。
请在下面找到演示,其中包含我提到的更改。另请注意,还有许多其他有效的方法可以让绑定发生,但我正在尝试用您提供的示例回答您的实际 OP。
import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
public class ObjectBinding_Demo {
public static void main(String[] args) {
new ObjectBinding_Demo().init();
}
private void init() {
A a = new A();
C c = new C();
ObjectBindingImpl binding = new ObjectBindingImpl(a);
c.c.bind(binding);
a.b = new B(20);
binding.obj.setValue(a.b); // Update the observable property with the new value
System.out.println("Value set in binding observable property..");
System.out.println(c.c); // Output:: IntegerProperty [bound, invalid]
System.out.println(c.c.get()); // Output:: 'Computing value...' string and then 20
System.out.println(c.c); // Output:: IntegerProperty [bound, value: 20]
}
class ObjectBindingImpl extends ObjectBinding {
private A a;
ObjectProperty obj; // Observable property reference for future updating.
public ObjectBindingImpl(A a) {
this.a = a;
obj = new SimpleObjectProperty(a.b);
bind(obj);
}
@Override
protected Object computeValue() {
System.out.println("Computing value...");
return a.b == null ? null : a.b.b;
}
}
class A {
B b = new B(10); // Assigned it value 10.
}
class B {
Integer b;
B(int b) {
this.b = b;
}
}
class C {
SimpleIntegerProperty c = new SimpleIntegerProperty();
}
}