JavaFX 强制重新计算绑定

5

我希望在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

当文本字段发生变化时,PathString会自动重新计算,并将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); 但这非常丑陋,显然不是正确的方法。我是否遗漏了什么,真的可以做到我想要的事情,或者根本没有这样的东西,我完全被困在这里?如果回答是否定的,那么我认为这严重损害了整个框架的表达能力。我理解我确实可以通过一些监听器来完成想要的操作,但这将非常丑陋,我想让整个过程尽可能通用。
2个回答

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);
    }
}

这也是我发现的问题,现在我正在使用类似的方法(通过低级绑定)。然而,这种方法似乎不适用于具有转换的双向绑定。对于我的用例来说,这不是问题,但我认为这对框架来说确实是个问题。我会接受你的答案,因为这基本上就是我现在正在使用的方法。谢谢。 - Vladimir Matveev
很好,你的问题已经解决了。关于框架问题,我建议在javafx-jira.kenai.com上提交问题,并/或者发送邮件至openjfx-dev@openjdk.java.net -- 这样可以直接联系到开发人员。 - Sergey Grinev

0
作为一种“丑陋”的解决方案,我最终随机更改值而不更改含义。例如,对于双倍值,我会添加一些很小的随机量以强制更改,或者对于字符串值,我会切换和隐藏空格。这一切都不是很好,但至少能够触发属性更改的效果。
在多线程环境下情况会变得更糟。如果绑定恰好发生在属性初始设置之后,绑定的属性将无法获得更改触发。如果您现在将属性设置为相同的值,则该值将被视为未更改,绑定的属性将无法获取初始值,而保持为 null 或默认值。非常丑陋!

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