#初めに
JavaFXのバインドにて、JavaFXのプロパティとバインドについて書きました。コンボボックスのバインドに手間取ったので、書いておきます。
#バインドさせるクラスを作成
コンボボックス内のデータ一つが一つのインスタンスになります。最初にコンボボックスに選択肢となるデータのクラスを作成します。もちろん選択肢専用でなく、通常のBeanでも大丈夫です。
例では、メニューのIDを表すIntegerとメニューの名前を表すStringのプロパティを持っています。
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.util.StringConverter;
public class CmbMenu {
private IntegerProperty id = new SimpleIntegerProperty();
public IntegerProperty idProperty(){return id;}
public int getId(){ return idProperty().get();}
public void setId(int id) { idProperty().set(id);}
private StringProperty title = new SimpleStringProperty();
public StringProperty titleProperty(){ return title;}
public String getTitle() { return titleProperty().get();}
public void setTitle(String title) { titleProperty().set(title);}
public CmbMenu(int id, String title){
idProperty().set(id);
titleProperty().set(title);
}
@Override
public String toString(){
return "CmbMenu[" + idProperty().get() + "," + titleProperty().get() + "]";
}
}
それから、このクラスを選択肢にどのように表示するのか、を表すクラスをStringConverterを継承して作成します。コンボボックスにどのように表示するか(toString)と、コンボボックスに自由入力された文字列をどのようなインスタンスにするか(fromString)を定義します。
public static class CmbMenuStringConverter extends StringConverter<CmbMenu>{
@Override
public String toString(CmbMenu object) {
if (object == null){ return "データなし";}
return object.getTitle();
}
@Override
public CmbMenu fromString(String string) {
CmbMenu cmbMenu = new CmbMenu(-1,string);
return cmbMenu;
}
}
#コンボボックスを使用する
private ObservableList<CmbMenu> data;
private ObjectProperty<CmbMenu> selectedData;
BooleanProperty blnBindBidirectional = new SimpleBooleanProperty();
@Override
public void initialize(URL paramURL, ResourceBundle paramResourceBundle) {
// コンボボックスのデータ
cmbBox.editableProperty().bind(blnBindBidirectional);
data = FXCollections.observableArrayList();
data.add(new CmbMenu(1,"A"));
data.add(new CmbMenu(2,"B"));
data.add(new CmbMenu(3,"C"));
cmbBox.setItems(data);
cmbBox.setConverter(new CmbMenu.CmbMenuStringConverter());
selectedData = new SimpleObjectProperty<>();
selectedData.bind(cmbBox.getSelectionModel().selectedItemProperty());
lblCmbBoxSelected.textProperty().bind(selectedData.asString());
cmbBox.getSelectionModel().selectedItemProperty().addListener((r,o,newValue) -> {
if (newValue == null){
lblCmbBox.textProperty().set("選択なし");
} else {
lblCmbBox.textProperty().set(newValue.getTitle());
}
});
}
ComboBox.setItemsで表示するデータを設定します。ObservableListなので、追加削除すると勝手にコンボボックスの内容に反映されて、素晴らしい。
ComboBox.setConverterでどのようにコンボボックスにデータを表示するか定義しているクラスを設定します。
ComboBox.editableProperty()でコンボボックスへの自由入力の可否を設定します。trueに設定すると、StringConverter.fromString(String)を入力した文字列を引数にして、selectedDataが取得できます。
ComboBox.getSelectionModel().SelectedItemProperty()でコンボボックスが変更されたら、他のラベルなどに自動通知で、表示が変更される。自由入力にした場合、Enterボタンを押すと選択されたことになる。
ComboBox.getSelectionModel().selectedItemProperty().addListener()でリスナーを追加できる。あんまり使いたくないですが。
#注意点
無駄に時間がかかったところ。
cmbBox.getSelectionModel().selectedItemProperty().addListener((r,o,newValue) -> {
if (newValue == null){
lblCmbBox.textProperty().set("選択なし");
} else {
コンボボックスが選択されていないと、newValueはnullになる。初期化時とか勝手に呼び出されるため(当然ですけど)、ぬるぽでスタックトレース吐いてました。
同様のことが CmbMenuStringConverter.toString(CmbMenu object)でも発生してました。
初期化時とかは引数がnullになるので、その点も考慮してプログラムする必要性をを感じました。
#まとめ
JavaFXのコンボボックスの選択肢は一つのクラスにして、選択肢一つがインスタンス一つになる。選択肢のリストはObservalListを設定して、追加削除を楽々実施。
コンボボックスへの実際の表示内容はStringConverterを利用して定義する。コンボボックスに直接入力時も同様。表示するデータと表示する内容を分けて定義できる。
ソースは、
https://github.com/xaatw0/quiita/tree/master/src/fxbind
にありますが、コンボボックス以外も混じってます。