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ずつ減り、counterLabelが自動的に更新されます。
現実
カウンターは1ずつデクリメントしますが、counterLabelは15(初期値)のままです。
質問
私は(たとえば、 このフォーラムの投稿 から)私がしたことはうまくいくはずだという印象を受けました。何が足りないのですか?
JavaFX固有のアクセサーvariableNamePropertyをコントローラーに追加する必要があります。
_public IntegerProperty counterProperty() {
return counter;
}
_
編集:詳細
APIドキュメントには、このJavaFXのJavaBeansアーキテクチャについてはあまり言及されていません。ここではそれについての紹介( JavaFXプロパティとバインディングの使用 )ですが、その必要性については何もありません。
では、ソースコードを掘りましょう! :)
簡単に、最初に FXMLLoaderコード を調べ始めます。バインディング式のプレフィックスは次のようになります。
_public static final String BINDING_EXPRESSION_PREFIX = "${";
_
さらに279行目で、FXMLLoaderはif (isBindingExpression(value))
を決定し、バインディングを作成し、BeanAdapter
をインスタンス化して、propertyModelを取得します。
_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()メソッドでは、リフレクションによって呼び出されたゲッター(counterProperty、getCounter、isCounterのいずれか)だけで、結果が返されます。ゲッターが存在しない場合、nullが返されます。つまり、「counterProperty()」がJavaBeanに存在しない場合、この場合はnullが返されます。この場合、FXMLLoaderのステートメントif (propertyModel instanceof Property<?>)
が原因で、バインディングは実行されません。その結果、「counterProperty()」メソッドにはバインディングがありません。
ゲッターがBeanで定義されていない場合はどうなりますか?再びBeanAdapter#get()
コードから、「getCounter()」が見つからない場合、nullが返され、呼び出し元はそれをno-opimoとして無視すると言うことができます。