【问题标题】:JavaFX complex string bindingJavaFX 复杂字符串绑定
【发布时间】:2023-03-16 07:20:01
【问题描述】:

我是 JavaFX 新手,想知道 Bindings API 是否允许更简单的方法来实现以下目标。考虑一个模型,它包含一个可能为 null 的数据库(因为数据库异步加载)和一个显示标签 status 反映数据库状态的视图。如果它为空,它应该说类似“正在加载...”,如果不是,它应该显示数据库中有多少项目。如果状态可以反映数据库在增长或缩小时的大小,那就太好了。

到目前为止,我知道我可以使用转换器将整数属性(数据库大小)绑定到标签的文本属性。这很好,但我希望标签显示的不仅仅是数字。像“Loaded {0} items”这样的本地化字符串。并且不要忘记数据库可能仍然为空。

这是我的解决方案

@Override
public void initialize(URL url, ResourceBundle bundle) {
    // Initialize label with default value
    status();
    model.databaseProperty().addListener((obs, old, neu) -> {
        // Update label when database is no longer null
        status();
        // Update label when size of database changes
        neu.sizeProperty().addListener(x -> status());
    });
}

public void status() {
    if (model.database() == null) {
        status.setText(bundle.getString("status.loading"));
    } else {
        String text = bundle.getString("status.ready");
        int size = model.database().size();
        text = new MessageFormat(text).format(size);
        status.setText(text);
    }
}

它可以工作,但是有没有办法用一连串的绑定来做到这一点,或者至少是其中的一部分?我已经看到布尔绑定有多么强大(和冗长),但我不确定字符串绑定是否可以实现如此灵活的功能。

【问题讨论】:

    标签: java javafx-8 javafx-bindings


    【解决方案1】:

    你可以使用Bindings.when,它本质上是一个动态的if/then绑定:*

    status.textProperty().bind(
        Bindings.when(model.databaseProperty().isNull())
            .then(bundle.getString("status.loading"))
            .otherwise(
                Bindings.selectInteger(model.databaseProperty(), "size").asString(
                    bundle.getString("status.ready")))
    );
    

    但是,以上假设bundle.getString("status.ready") 返回一个 java.util.Formatter 字符串,而不是 MessageFormat 字符串。换句话说,它需要是"Loaded %,d items" 而不是"Loaded {0,number,integer} items"

    Bindings 没有对 MessageFormat 的内置支持,但如果您真的想坚持使用 MessageFormat(这是一个合法的要求,因为 MessageFormat 可以做 Formatter 不能做的事情),您可以使用Bindings.createStringBinding:

    MessageFormat statusFormat = new MessageFormat(bundle.getString("status.ready"));
    
    status.textProperty().bind(
        Bindings.when(model.databaseProperty().isNull())
            .then(bundle.getString("status.loading"))
            .otherwise(
                Bindings.createStringBinding(
                    () -> statusFormat.format(new Object[] { model.getDatabase().getSize() }),
                    model.databaseProperty(),
                    Bindings.selectInteger(model.databaseProperty(), "size")))
    );
    

    * 实际上,它更像是三元 ?: 运算符。

    【讨论】:

    • 很好的答案,谢谢。据我了解select 只会匹配getSize() 方法而不是size() 方法,对吗?有什么办法可以改变这种行为?
    • 如果数据库类添加了public IntegerProperty sizeProperty() 方法,那可能就足够了。如果无法向数据库类添加方法,可以将 Bindings.selectInteger 替换为 Bindings.createIntegerBinding,类似于 createStringBinding 的工作方式,但如果不将一些表示大小的 Observable 值传递给 createIntegerBinding,自定义绑定将不会知道在大小变化时自我更新。
    • 经过进一步考虑,我意识到应该是public ReadOnlyIntegerProperty sizeProperty()。还有另一种表示 size 属性的可能性,尽管这需要更多的努力:您可以为数据库类创建一个自定义 Beaninfo,如the Introspector documentation 中所述,以告诉 bean 属性发现过程对应于每个方法的方法类的属性。
    • 这可能是某些属性所必需的。我目前对select 根本无法匹配ObservableListsorted() 方法这一事实感到困惑。具体来说,select(object, "list", "sorted") 会引发异常,即使 objectlistProperty()getList() 方法。
    • 澄清一下,((ObservableList) Bindings.select(object, "list")).get().sorted() 还是可以的,但是有点冗长
    猜你喜欢
    • 1970-01-01
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 2022-08-07
    • 2015-09-18
    • 2012-09-10
    相关资源
    最近更新 更多