我想在JavaFX中实现类似函数式反应式编程的东西,我认为既然JavaFX已经支持属性之间的监听器和绑定,那么它应该相当容易,所以我创建了一个用于转换绑定的小框架,例如:我现在能够做这样的事情(Scala中的例子,但应该可以理解我的意思):
val property1: Property[String]
val property2: Property[Path]
Bindings.Conversions
.bindUni(property1).to(property2)
.using(p => p.getFileName.toString)
.connect()
这里我通过转换函数绑定property2
的值(java.nio.file.Path
),转换函数占用路径的最后部分并转换为字符串property1
(这是字符串)。
这个实现非常简单(即使对于双向绑定;我只是从openjfx BidirectionalBinding
类中获取一些代码,将其转换为Scala并将其调整为转换),我想知道为什么没有这样的东西在JavaFX中已经。
这一切都运作良好,我甚至可以创建这种绑定的复杂链。除非转换功能取决于某些外部状态,否则一切正常。
例如,假设您有以下绑定链:
Text field value -1-> intermediate java.nio.file.Path -2-> another String --> Label
当文本字段更改时,Path
和String
会自动重新计算,并且String属性的值将写入标签。一切都很棒。但是假设-2->
转换应该取决于某些复选框的切换状态:
Checkbox state ---+
|
Text field value -1-> intermediate java.nio.file.Path -2-> another String --> Label
也就是说,选中复选框后,转换应略有不同。
显然,直接实现此类构造不起作用,因为更改复选框状态不会切换转换链的重新计算。但是,我发现JavaFX没有提供任何强制更改事件的方法。例如,我尝试覆盖SimpleStringProperty
并公开其fireValueChangedEvent()
方法,但这没有用。目前我正在做类似textField.setText(""); textField.setText(oldValue);
的事情,但这显然是非常丑陋,显然不是正确的方法。
我错过了什么,我真的可以做我想做的事情,或者根本没有这样的事情,我完全搞砸了?
如果答案是否定的,那么我认为这严重损害了整个框架的表达能力。我知道我真的可以和很多听众做我想做的事情,但这很丑陋,而且我想让整件事尽可能一般。
答案 0 :(得分:1)
您可以像收听字符串属性一样收听CheckBox#selectedProperty()
。见下一个例子:
public class ConditionalBind extends Application {
Label label = new Label();
TextField tf = new TextField("hi");
CheckBox cb = new CheckBox("lowerize");
@Override
public void start(Stage primaryStage) {
label.textProperty().bind(new StringBinding() {
{
bind(tf.textProperty(), cb.selectedProperty());
}
@Override
protected String computeValue() {
return cb.isSelected() ? tf.getText().toLowerCase() : tf.getText();
}
});
VBox root = new VBox(10);
root.getChildren().addAll(label, cb, tf);
primaryStage.setScene(new Scene(root, 300, 250));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
答案 1 :(得分:0)
正如“丑陋”的解决方法一样,我最终随意改变了值而没有改变含义。例如。对于一个double值,我会添加一个小的随机数量来强制改变或一个字符串值我切换和不可见空间。这一切都不好,但会起到触发属性更改的效果。
在多线程环境中情况变得更糟。在那里,如果绑定恰好在属性的初始设置之后稍微发生,则可能发生有界属性不会获得更改触发器。如果现在将属性设置为相同的值,则该值将被视为未更改,并且有界属性将不会获取初始值,而是保持为null或default值。非常难看!