はじめに
今回のサンプルコードはGitHubにあります。
コミット履歴で内容を追えるようにしてあります。
コンボボックスの選択肢を列挙型で表示したい
Java1.5より使える列挙型にて、手軽に一連の値を定義することができます。
そしてそのままコンボボックスの選択肢として並べて表示したいのはごく自然なことですね。
package com.exsample.javafxlocatecombo;
public enum ParakeetEnum {
PEACH_FACED_LOVEBIRD,
COCKATIEL,
BUDGERIGAR;
}
package com.exsample.javafxlocatecombo;
import java.net.URL;
import java.util.Objects;
import java.util.ResourceBundle;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
public class MainSceneFXMLController implements Initializable {
@FXML
private ComboBox<ParakeetEnum> comboParakeet;
@FXML
private Label label;
@Override
public void initialize(URL url, ResourceBundle rb) {
ObservableList<ParakeetEnum> list =
FXCollections.observableArrayList(ParakeetEnum.values());
comboParakeet.getItems().addAll(list);
comboParakeet.valueProperty().addListener(
(ObservableValue<? extends ParakeetEnum> observable,
ParakeetEnum oldValue, ParakeetEnum newValue) -> {
if (Objects.nonNull(newValue)) {
label.setText(newValue.name());
}
});
}
}
上記は、コンボボックスの値が変わったら、それをラベルに表示するサンプルです。
- ComboBoxの総称型には、作成した列挙型を指定します
- FXCollections.observableArrayListに列挙型の配列を入れます
- ComboBoxの valueProperty().addListener() で値の変更を受けています
- なお列挙型の values() や name() は暗黙に定義されているメソッドです
で、画面には列挙型の定義名がそのまま表示されるので悲しい感じになります。
列挙型を使いつつも、狙った文字列を表示する技
Javaの列挙型はフィールドやメソッドを定義できます。
ここに表示したい文字列を入れてあげます。
package com.exsample.javafxlocatecombo;
public enum ParakeetEnum {
PEACH_FACED_LOVEBIRD("小桜インコ"),
COCKATIEL("オカメインコ"),
BUDGERIGAR("セキセイインコ");
final private String name;
private ParakeetEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
表示側のコンボボックスは少し面倒ですが、レンダリングをカスタマイズしてあげます。
ここで注意なのが、レンダリングは「ListView」と「Button」が別々ということです。
よく setCellFactory() だけを実装している例がありますが、それでは「ListView」しかレンダリングしません。
そしてレンダリングの setText() やラベルを更新するときに列挙型のメソッド getName() を呼んでいます。
package com.exsample.javafxlocatecombo;
import java.net.URL;
import java.util.Objects;
import java.util.ResourceBundle;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.util.Callback;
public class MainSceneFXMLController implements Initializable {
@FXML
private ComboBox<ParakeetEnum> comboParakeet;
@FXML
private Label label;
@Override
public void initialize(URL url, ResourceBundle rb) {
Callback<ListView<ParakeetEnum>, ListCell<ParakeetEnum>> cellFactory
= (ListView<ParakeetEnum> param) -> new ListCell<ParakeetEnum>() {
@Override
protected void updateItem(ParakeetEnum item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
setText(item.getName());
}
}
};
ObservableList<ParakeetEnum> list
= FXCollections.observableArrayList(ParakeetEnum.values());
comboParakeet.getItems().addAll(list);
comboParakeet.setButtonCell(cellFactory.call(null));
comboParakeet.setCellFactory(cellFactory);
comboParakeet.valueProperty().addListener((
ObservableValue<? extends ParakeetEnum> observable,
ParakeetEnum oldValue, ParakeetEnum newValue) -> {
if (Objects.nonNull(newValue)) {
label.setText(newValue.getName());
}
});
}
}
列挙型のコンストラクタに入れた文字が表示されました。
多言語対応で極める!!
GUIアプリケーションなので多言語対応したいことはあるでしょう。
上記まではお決まりのパターンなので、探せば解説がありますが、今回の多言語対応はオリジナル実装です。でも外していないと思います。
まずロケールファイルを用意します。好きなだけ用意してください。
key.PeachFacedLovebird=Peach-faced Lovebird
key.Cockatiel=Cockatiel
key.Budgerigar=Budgerigar
key.PeachFacedLovebird=小桜インコ
key.Cockatiel=オカメインコ
key.Budgerigar=セキセイインコ
列挙型はリソースキーを定義するように変更します。
package com.exsample.javafxlocatecombo;
public enum ParakeetEnum {
PEACH_FACED_LOVEBIRD("key.PeachFacedLovebird"),
COCKATIEL("key.Cockatiel"),
BUDGERIGAR("key.Budgerigar");
final private String resourceKey;
private ParakeetEnum(String resourceKey) {
this.resourceKey = resourceKey;
}
public String getResourceKey() {
return resourceKey;
}
}
画面は ResourceBundle.getString() で指定したリソースキーの文字を取得するように変えます。
@Override
public void initialize(URL url, ResourceBundle rb) {
Callback<ListView<ParakeetEnum>, ListCell<ParakeetEnum>> cellFactory
= (ListView<ParakeetEnum> param) -> new ListCell<ParakeetEnum>() {
@Override
protected void updateItem(ParakeetEnum item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
setText(rb.getString(item.getResourceKey()));
}
}
};
ObservableList<ParakeetEnum> list
= FXCollections.observableArrayList(ParakeetEnum.values());
comboParakeet.getItems().addAll(list);
comboParakeet.setButtonCell(cellFactory.call(null));
comboParakeet.setCellFactory(cellFactory);
comboParakeet.valueProperty().addListener((
ObservableValue<? extends ParakeetEnum> observable,
ParakeetEnum oldValue, ParakeetEnum newValue) -> {
if (Objects.nonNull(newValue)) {
label.setText(rb.getString(newValue.getResourceKey()));
}
});
}
画面を用意する際に FXMLLoader.load() に ResourceBundle を指定してあげます。
ロケールの切り替えは Locale.setDefault() で行っていますが、これは設定ファイルなり設定ダイアログなり、システム設定に任すなり、好きにしてください。サンプルで切り替えて確認できるようにしてあるだけです。
package com.exsample.javafxlocatecombo;
import java.util.Locale;
import java.util.ResourceBundle;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainApp extends Application {
@Override
public void start(Stage stage) throws Exception {
Locale.setDefault(Locale.ENGLISH);
ResourceBundle rb = ResourceBundle.getBundle("locale.locale", Locale.getDefault());
Parent root = FXMLLoader.load(getClass().getResource("/fxml/MainScene.fxml"), rb);
Scene scene = new Scene(root);
scene.getStylesheets().add("/styles/Styles.css");
stage.setTitle("JavaFX and Maven");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
出ましたー。