将标签的文本属性(在FXML文件中)绑定到控制器中的IntegerProperty

15

我已经在一个FXML文件中的Label和相关控制器中的IntegerProperty之间建立了数据绑定。问题是,尽管标签在初始化时被设置为正确的值,但当属性的值发生变化时,它不会更新。

FXML文件

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<GridPane xmlns:fx="http://javafx.com/fxml"
   fx:controller="application.PaneController" minWidth="200">
   <Label id="counterLabel" text="${controller.counter}" />
   <Button translateX="50" text="Subtract 1"
      onAction="#handleStartButtonAction" />
</GridPane>

控制器

package application;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;

public class PaneController implements Initializable
{
    private IntegerProperty counter;

    public int getCounter()
    {
        return counter.get();
    }

    public void setCounter(int value)
    {
        counter.set(value);
    }

    public PaneController()
    {
        counter = new SimpleIntegerProperty(15);
    }

    @Override
    public void initialize(URL url, ResourceBundle resources)
    {
    }

    @FXML
    private void handleStartButtonAction(ActionEvent event)
    {
        setCounter(getCounter() - 1);
        System.out.println(getCounter());
    }
}

期望

每次我按下“减1”按钮,计数器将自动减1,并且计数器标签将自动更新。

现实

计数器确实减少了1,但计数器标签仍停留在15(初始值)。

问题

我原本以为(例如,从这个论坛帖子中获得的信息)我所做的应该有效。我漏掉了什么?

1个回答

40

您需要在控制器中添加一个JavaFX特定的访问器variableNameProperty

public IntegerProperty counterProperty() {
    return counter;
}

编辑:更多细节。
API文档对JavaFX的JavaBeans架构只有简短介绍。这里 (Using JavaFX Properties and Binding) 只是一个简单的介绍,但没有提及其必要性。

所以让我们来看一下源代码! :)
直接地,我们首先查看FXMLLoader代码。我们注意到绑定表达式的前缀为:

public static final String BINDING_EXPRESSION_PREFIX = "${";

在第279行,FXMLLoader进一步确定了value是否为绑定表达式,如果是,则创建绑定,实例化BeanAdapter并获取属性模型。
BeanAdapter targetAdapter = new BeanAdapter(this.value);
ObservableValue<Object> propertyModel = targetAdapter.getPropertyModel(attribute.name);

如果我们查看BeanAdapter#getPropertyModel()

public <T> ObservableValue<T> getPropertyModel(String key) {
    if (key == null) {
        throw new NullPointerException();
    }
    return (ObservableValue<T>)get(key + BeanAdapter.PROPERTY_SUFFIX);
}

在添加了 String PROPERTY_SUFFIX = "Property"; 后,它委托给BeanAdapter#get()。在get()方法中,通过反射调用简单的getter方法(counterProperty、getCounter或isCounter),将结果返回。如果getter方法不存在,则返回null。换句话说,在JavaBean中不存在"counterProperty()"时,在我们的情况下返回null。在FXMLLoader中由于语句if (propertyModel instanceof Property<?>)的存在,绑定不会执行。结果就是没有"counterProperty()"方法,也没有绑定。

如果bean中未定义getter方法会发生什么?同样从BeanAdapter#get()代码中可以看出,如果找不到"getCounter()",则返回null,调用者将其忽略为no-op。


我之前尝试过,但是它没有起作用,因为我还将我的FXML标记更改为${controller.counterProperty}。如果我将其保留为${controller.counter},它就可以工作了。非常感谢! - devuxer
2
此外,有趣的是,如果我删除对getCounter()setCounter()的定义(直接使用counter字段),它会失败。这是否意味着控制器上的getter/setter被绑定机制使用?(出乎意料的是,我需要为属性提供getter以及为属性值提供getter/setter。) - devuxer
谢谢你的更新,Uluk!这绝对有助于更好地理解系统。如果可以的话,我会再给你一个+1。 - devuxer
1
非常感谢您的回答。在我的FXML文件中,IntelliJ中的控制器关键字是红色的,显示错误消息“无法解析符号'controller'”。但是当我运行它时 - 一切都正常工作。似乎是IntelliJ集成失败了。有人知道如何解决这个问题吗? - JuHwon
1
我觉得我的潜意识不满意我尝试JavaFX:有一瞬间,我把“BeanAdapter#get()”误读为“BeanAdapter.regret()”… - tevemadar
显示剩余2条评论

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