正如已经说过的,没有简单的方法可以从 FXML 进行双向绑定。然而,它可以从 FXML 完成,但不能使用 FXML 本身,可以这么说。这是我的发现。
<fx:script> 块
第一种也是最明显的方法是使用<fx:script> 通过 JavaScript 进行所需的任何绑定。例如,可以使用以下 sn-p 进行双向绑定:
<?language javascript?>
<!-- ... -->
<TextField fx:id="aControl"/>
<fx:script>
javafx.beans.binding.Bindings.bindBidirectional(
controller.someProperty(),
aControl.textProperty()
);
</fx:script>
这里的someProperty() 是一个名为some 的属性的控制器方法。
自定义帮助类
第二个选项是创建一个辅助类,然后在 <fx:define> 块中使用它(使用上面的约定):
<?import u7n.examples.BidiBind?>
<!-- ... -->
<TextField fx:id="aControl"/>
<fx:define>
<BidiBind o1="$controller" p1="some" o2="$aControl" p2="text"/>
</fx:define>
下面是这样一个类的示例实现:
package u7n.examples;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
public class BidiBind {
private Object o1;
private String p1;
public Object getO1() { return o1; }
public void setO1(Object o1) { this.o1 = o1; bind(); }
public String getP1() { return p1; }
public void setP1(String p1) { this.p1 = p1; bind(); }
private Object o2;
private String p2;
public Object getO2() { return o2; }
public void setO2(Object o2) { this.o2 = o2; bind(); }
public String getP2() { return p2; }
public void setP2(String p2) { this.p2 = p2; bind(); }
private <T> void bind() {
if (o1 == null || p1 == null || o2 == null || p2 == null) return;
try {
@SuppressWarnings("unchecked")
Property<T> property1 = (Property<T>)o1.getClass().getMethod(p1 + "Property").invoke(o1);
@SuppressWarnings("unchecked")
Property<T> property2 = (Property<T>)o2.getClass().getMethod(p2 + "Property").invoke(o2);
Bindings.bindBidirectional(property1, property2);
}
catch (Exception e) {
e.printStackTrace();
}
o1 = o2 = null;
p1 = p2 = null;
}
}
它使用反射来访问属性,因为我还没有找到让 FXMLLoader 将属性而不是其值传递给 setter 的方法。
总结一下
这两种方式都不理想,但可以从 FXML 进行双向绑定,尽管方式不方便。当然,像
<TextField fx:id="aControl" text="#{controller.some}"/>
会更好,但唉。我也不知道这两种提议的方法在 Scene Builder 上的效果如何,因为我不使用它。