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

dev*_*xer 12 java data-binding javafx fxml

我已经在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>
Run Code Online (Sandbox Code Playgroud)

调节器

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

期望

每按一次"Subtract 1"按钮,计数器将减1,counterLabel将自动更新.

现实

计数器确实递减1,但counterLabel仍然停留在15(初始值).

我的印象是(例如,来自这个论坛帖子),我所做的应该是有效的.我错过了什么?

Ulu*_*Biy 35

您需要将一个JavaFX特定的访问者variableName 属性添加到控制器:

public IntegerProperty counterProperty() {
    return counter;
}
Run Code Online (Sandbox Code Playgroud)

编辑:更多细节.
API文档并未提及此JavaFX的JavaBeans体系结构.这里只介绍一下它(使用JavaFX属性和绑定),但同样没有关于它的必要性.

所以让我们挖一些源代码吧!:)
直截了当,我们首先开始研究FXMLLoader代码.我们注意到绑定表达式的前缀为

public static final String BINDING_EXPRESSION_PREFIX = "${";
Run Code Online (Sandbox Code Playgroud)

在第279行,FXMLLoader确定if (isBindingExpression(value))然后创建绑定,实例化BeanAdapter并获取propertyModel:

BeanAdapter targetAdapter = new BeanAdapter(this.value);
ObservableValue<Object> propertyModel = targetAdapter.getPropertyModel(attribute.name);
Run Code Online (Sandbox Code Playgroud)

如果我们看看BeanAdapter#getPropertyModel(),

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

BeanAdapter#get()在追加后委托给String PROPERTY_SUFFIX = "Property";
get()方法,只是反射调用的getter(counterProperty或getCounter或isCounter),返回结果.如果getter不存在,则返回null.换句话说,如果JavaBean中不存在"counterProperty()",则在我们的情况下返回null.在这种情况下,由于语句,不执行绑定if (propertyModel instanceof Property<?>) FXMLLoader中,.结果,没有"counterProperty()"方法没有绑定.

如果没有在bean中定义getter会发生什么?再次从BeanAdapter#get()代码我们可以说,如果找不到"getCounter()",则null返回并且调用者只是将其忽略为no-op imo.

  • 此外,有趣的是,如果我删除 `getCounter()` 和 `setCounter()` 的定义(并且直接使用 `counter` 字段),它就会失败。这是否意味着控制器上的 getter/setter 被绑定机制使用?(出乎意料的是,我需要为 *property* 提供一个 getter 以及为该属性的 *value* 提供一个 getter/setter。) (2认同)