プロジェクト

全般

プロフィール

JavaFXとバインディング

はじめに

JavaFXでは、コントロール等の持つ属性(ラベルのテキストなどの値や、幅・高さ、ディセーブル等)を「プロパティ」という概念を導入して他のオブジェクトの属性と「バインディング」し、片方の属性が変更されるとバインディングした他方の属性を連動して変更されるようにする仕組みが提供されています。

Swingまでの仕様・設計では、オブジェクトの属性の変更を連動させたいときはリスナーを設定してコールバックを受ける方法で実現していました。そのためにはリスナーオブジェクトを生成し(クラスにリスナーインタフェースをimplementsするか、リスナーインタフェースをimplemetsする内部クラスを作るか、リスナーインスタンスを匿名クラスでインスタンス化する)、リスナーメソッドで値を取得して連動させる箇所にセットするという手間をかけていました。

JavaFXでは、その仕組みが簡単にかけるようになりました、というところです。このプロパティという概念は馴染みがないと最初違和感、異質感が半端ないですが、馴染むとそれなりに受け入れて使えるようになります。

基本

TBD

やりたいこと別

型の違うプロパティをバインディングしたい

数値から文字列へのバインディング

例えば、スライダー(Slider)の値をラベルに表示したいとします。スライダーの値はDoubleProperty型で、ラベル(Label)の値はStringProperty型です。これを直接バインドするとコンパイルエラーとなってしまいます。

Slider slider;
Label label;
    :
label.textProperty().bind(slider.valueProperty());  // コンパイルエラー

数値型のプロパティのasStringメソッド

数値型のプロパティには、StringBindingを返却するasStringメソッドが用意されています。次似シグニチャを示します。

public StringBinding asString()
public StringBinding asString(String format)
public StringBinding asString(Locale locale, String format)

引数なしのasStringは、デフォルトの文字列(数値型プロパティの値をtoString()したもの)を生成します。

label.textProperty().bind(slider.valueProperty().asString());

書式を指定して文字列化する場合は、次のコードとなります。

label.textProperty().bind(slider.valueProperty().asString("%6.2f"));

Bindings.convertメソッドを使う

Bindingsクラスのメソッドconvertは、各種型のプロパティから文字列表現を生成するStringExpressionインスタンスを生成します。
シグニチャを次に示します。

public static StringExpression convert(ObservableValue<?> observableValue)

JavaFXの実装では、プロパティの実の値にtoString()を呼び出しています。

コードは次となります。

label.textProperty().bind(Bindings.convert(slider.valueProperty()));

Bindings.formatメソッドを使う

Bindingsクラスのメソッドformatは、第1引数に書式を指定し、第2引数にプロパティ(ObservableValue型)を指定すると、第2引数のプロパティが変化すると対応する文字列が生成されます。シグニチャを次に示します。

public static StringExpression format(String format, Object... args)

コードは次となります。

label.textProperty().bind(Bindings.format("%6.2f", slider.valueProperty()));

ラベルの文字列プロパティにNumberStringConverterを使ってスライダーの値プロパティをバインド
label.textProperty().bindBidirectional(slider.valueProperty(), new NumberStringConverter());
BindingsクラスのbindBidirectionalメソッドを用い、NumberStringConverterを第3引数に指定
Bindings.bindBidirectional(label.textProperty(), slider.valueProperty(), new NumberStringConverter());
  • 注記
    最初、バインドする数値のプロパティがDoubleProperty型だからという理由でDoubleStringConverterを使ったのですが、コンパイルエラーになってしまいました。DoubleProperty型は、Property<java.lang.Number>をimplementsしているので、NumberStringConverterでないと合致しないためと思われます。
BindingsクラスのcreateStringBindingメソッドでStringへのBindingインスタンスを生成
label.textProperty().bind(
    Bindings.createStringBinding( () -> Double.toString(slider.getValue()), slider.valueProperty())
);

createStringBindingメソッドのシグニチャは次です。

public static StringBinding createStringBinding(Callable<String> func, Observable... dependencies)

第1引数に、プロパティが変化したときに実行する処理(戻り値型がString)を記述したCallable(ラムダ式でも可)を指定、 第2引数に変化を監視するプロパティを指定します。

値を計算した結果でバインディングしたい

数値のプロパティに定数を加減算してバインディング

ある数値プロパティに定数を足す、または引いて別な数値プロパティに結び付けます。

@FXML
private AnchorPane rootPane;
@FXML
private Canvas canvas;

@Override
public void initialize(URL url, ResourceBundle rb) {
    canvas.widthProperty().bind(rootPane.widthProperty().subtract(200));
    canvas.heightProperty().bind(rootPane.heightProperty().subtract(120));
}

クリップボードから画像を追加 (サイズの上限: 1 GB)