ユーザーに情報を提示したり、確認をする時に使用する、ダイアログボックスとして利用できる、JavaFX の Alert、Dialog、Stage について具体例を通じて説明する。ここでいうダイアログボックスとは、そのダイアログボックスが表示されたらそれを閉じなければ元のアプリケーションに戻れない、 ウィンドウのことである。作成した具体例は AlertDialogAndStage である。
Alert にはINFORMATION、CONFIRMATION、WARNING、ERRORの4種類があり、それがどういうものであるか?が「Alert」ボタンを押せば表示される。また、既定のボタンを変更する方法も説明する。
Alert では、複数のボタンがある場合に、どのボタンが押されたかを知ることはできるが、それ以上のことを知ることはできない。そこで、好きな色を聞く Dialog を作成した。「Dialog」ボタンを押すと、カラーピッカーで好きな色を答えることが出来る。
Stage を利用しても、Dialog と同じように振る舞えるように、設定できる。ここでは、例として、「Alert, Dialog and Stage」の情報を表示させる。
Scene Builder で作成した AlertDialogAndStage のメインウィンドウの主要部分(VBox)は次の通りである。ファイル名は AlertDialogAndStageMainVBox.fxml である。(Eclipse のプロジェクト src の zip ファイルはこちら。)
前回と同様に作成した VBox のコントローラーを VBox の派生クラスとするので、Controller class に何も記入せずに、Use fx:root construct をチェックする。このコントローラーのファイル名は AlertDialogStageMainVBox.java である。以降に、更に2回 Scene Builder で作業を行うが、このチェックする図は省略する。
Scene Builder で作成した、「Dialog」ボタンを押したときに表示される Dialog の主要部分(VBox)と「Stage」ボタンを押したときに表示される Stage は次の通りである。各々、ファイル名は DialogFavoriteColorVBox.fxml と StageAbout.fxml である。また、コントローラーのファイル名は DialogFavoriteColorVBox.java と StageAbout.java である。
AlertDialogAndStage プロジェクトの Main クラスに関しては、ほぼ、前回と同様なので、説明を省略する。メインウィンドウの主要部分である VBox のコントローラーである AlertDialogAndStageMainVBox の説明を行う。(このコントローラークラスのコンストラクターもほぼ、前回と同様なので、説明を省略する。)重要な部分は、コンストラクタ内の fxmlLoader.load() 辺りで呼ばれる、@FXML の次行にある private void initialize() 関数である。この関数内で、comboBoxAlertType の初期化とボタン押下時のイベントハンドラを記述する。
上図において、イベントハンドラを記述するボタンは、上から順に、
- コンボボックス comboBoxAlertType からアラートのタイプを選び、「既定のボタン」にするか否か(checkBoxDefault)を選び、それらにより決まったアラートを表示し、CONFIRMATION の場合は押されたボタンを表示する「Alert」ボタン(buttonAlert)
- 「色の選択ダイアログ」を表示し、選ばれた色を表示する「Dialog」ボタン(buttonDialog)
- 「…についてウィンドウ」を表示する「Stage」ボタン(buttonStage)
(1)
まず、Alert の基本的なことについて述べる。タイプとして、主に、AlertType.INFORMATION、AlertType.CONFIRMATION、AlertType.WARNING、AlertType.ERROR の4つがあり、表示用の title、header、content の文字列を設定できる。また、既定のボタンがある。CONFIRMATION には「OK」と「取消」の2個のボタンがあるが、その他は「OK」ボタン1個である。以上が、基本的なことである。
ここでは、titles をタイプの和訳、headers と contents は次のようにした。
private String[] titles = {"情報", "確認", "警告", "エラー"}; private String[] headers = {"今年の西暦", "下記の確認", "利用時間に関する警告", "エラーです"}; private String[] contents = {"今年は2020年です。", "入力されたお名前は 〇〇 様です。\n正しければ「OK」を間違っていれば「取消」を押して下さい。", "長時間利用されています。一度終了し、休息をお取りください。", "0では割れません。"};
上記の配列を有効に利用できるように、comboBoxAlertType 設定用の stringAlertTypes とそれに応じた alertTypes を次のようする。
private String[] stringAlertTypes = {"INFORMATION", "CONFIRMATION", "WARNING", "ERROR"}; private AlertType[] alertTypes = {AlertType.INFORMATION, AlertType.CONFIRMATION, AlertType.WARNING, AlertType.ERROR};
具体的なコードは、
@FXML private void initialize() { comboBoxAlertType.getItems().setAll(stringAlertTypes); comboBoxAlertType.setValue("INFORMATION"); setHandlers(); } private void resetResult() { rectangle.setVisible(false); labelResult.setTextFill(Color.BLACK); labelResult.setText(""); }
である。関数 initialize() では、コンボボックスを初期化し、関数 setHandlers() 内で buttonAlert、buttonDialog、buttonSage ボタン押下時のイベントハンドラを記述する。関数 resetResult() では、「Alert」ボタンと「Dialog」ボタンの結果を表示する四角(rectangle)を非表示にし、ラベル(labelResult)の文字の色を黒に戻し、空文字列に戻す。
setHandlers() の下記のコ-ドでコメント
- // 適切なアラートの表示と後処理
- // 色の選択ダイアログの表示と後処理
- // …についてステージの表示
private void setHandlers() { buttonAlert.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { resetResult(); // 適切なアラートの表示と後処理 } }); buttonDialog.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { resetResult(); // 色の選択ダイアログの表示と後処理 } }); buttonStage.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { resetResult(); // ...についてステージの表示 } }); }
適切なアラートの表示と後処理
まず、コンボボックスで選ばれてるインデックスを調べ、それに応じて適切なタイプのアラートを alert にインスタンス化し、対応する titles、headers、contents を設定する。
int i = comboBoxAlertType.getSelectionModel().getSelectedIndex(); Alert alert = new Alert(alertTypes[i]); alert.setTitle(titles[i]); alert.setHeaderText(headers[i]); alert.setContentText(contents[i]);
次に、「既定のボタン」(checkBoxDefault)がチェックされていなければ、CONFIRMATION のボタンを「はい」と「いいえ」に、その他のボタンを「了解」に変更する(それに応じて contents の内容も「はい」と「いいえ」に変更する)。
if (!checkBoxDefault.isSelected()) { if (i == 1) { // AlertType.CONFIRMATION ButtonType buttonTypeYes = new ButtonType("はい", ButtonData.YES); ButtonType buttonTypeNo = new ButtonType("いいえ", ButtonData.NO); alert.getButtonTypes().setAll(buttonTypeYes, buttonTypeNo); alert.setContentText("入力されたお名前は 〇〇 様です。\n正しければ「はい」を間違っていれば「いいえ」を押して下さい。"); } else { // other than AlertType.CONFIRMATION ButtonType buttonType = new ButtonType("了解"); alert.getButtonTypes().setAll(buttonType); } }
ボタンを変更する場合は、新しいボタンを作成し、それを
alert.getButtonTypes().setAll(/*新しいボタン*/);
とすれば良い。後で押されたボタンを調べる時のために、新しいボタン作成時の第2引数である ButtonData を覚えておく。
以上で、表示すべき alert の設定を終えたので、表示して応答を(ボタンが押されるか右上の閉じるボタンが押されるまで)待つ。CONFIRMATION 以外は後処理をしなくて良い。CONFIRMATION の場合は、押されたボタンを表示する。押されたボタンを調べるために、応答があれば(result.isPresent() が真の場合)、押されたボタンのタイプ(result.get())を、まず、既定のボタンのタイプである ButtonType.OK か ButtonType.CANCEL のどちらであるか調べる。もし、どちらでもなければ、新たに作成したボタンタイプである。この場合は、更に、ボタンデータ(result.get().getButtonData())まで調べる。すなわち、上記で作成した ButtonData.YES か ButtonData.NO のどちらであるかを調べる。押下されたボタンが分かったので、それを表示用のラベルに設定する。例えば、
labelResult.setText(“「はい」ボタンが押されました。”);
である。実際のコードは
Optional<ButtonType> result = alert.showAndWait(); if (i == 1) { // AlertType.CONFIRMATION if (result.isPresent()) { if (result.get() == ButtonType.OK) { labelResult.setText("「OK」ボタンが押されました。"); } else if (result.get() == ButtonType.CANCEL) { labelResult.setText("「取消」ボタンが押されました。"); } else if (result.get().getButtonData() == ButtonData.YES) { labelResult.setText("「はい」ボタンが押されました。"); } else if (result.get().getButtonData() == ButtonData.NO) { labelResult.setText("「いいえ」ボタンが押されました。"); } } }
となる。
(2)
色の選択ダイアログの表示と後処理
色の選択ダイアログ DialogFavoriteColor の内容は後で述べるが、Color を戻り値として返す Dialog である。まず、DialogFavoriteColor の新しいインスタンスを作り表示し、応答を待つ。応答の内容を調べるために、応答があれば(result.isPresent() が真の場合)、戻り値の Color(result.get())を調べ、color に代入する。この色を表示する rectangle を可視状態にし、色を color に設定し、labelResult に「色の値」をこの色で表示する。color.toString() で 0x で始まる16進数で色の値が得られるが、ダイアログ内の ColorPicker の表示と合わせるために、「0x」を「#」に変更し、最後の2桁を捨てる。すなわち、index が2(を含み)から8(を含まない)までを利用する。実際のコードは次の通りである。
DialogFavoriteColor dialogFavoriteColor = new DialogFavoriteColor(); Optional<Color> result = dialogFavoriteColor.showAndWait(); if (result.isPresent()) { Color color = result.get(); rectangle.setVisible(true); rectangle.setFill(color); rectangle.setStroke(color); labelResult.setTextFill(color); labelResult.setText("好きな色として #" + color.toString().substring(2, 8) + " が選ばれました。"); }
色の選択ダイアログ DialogFavoriteColor に関して述べる。ダイアログは下図の通りで、赤で囲んだ部分が DialogFavoriteColorVBox である。
この DialogFavoriteColorVBox の Scene Builder での作業図については上述した。この VBox の派生クラスであるコントローラー DialogFavoriteColorVBox.java に関しては、下記のように colorPicker の色を返す getter を記述しておく。
public Color getColor() { return colorPicker.getValue(); }
さて、Dialog を利用する状況は、ユーザーから何かの情報を得る、ことである。ここではユーザーが選んだカラーピッカーの色である。その場合 DialogFavoriteColor クラスは Dialog の派生クラスで戻り値が
public class DialogFavoriteColor extends Dialog<Color> { private DialogPane dialogPane = getDialogPane(); public DialogFavoriteColor() { // 必要な処理を記述する } }
ただし、後で必要になるこの Dialog の表示部分 DialogPane を dialogPane に代入しておいた。後は、コンストラクタ内で必要な処理をすれば良い。まず、上述の DialogFavoriteColorVBox の新しいインスタンスを作り、ダイアログの表示部に入れる。
DialogFavoriteColorVBox dialogFavoriteColorVBox = new DialogFavoriteColorVBox(); dialogPane.setContent(dialogFavoriteColorVBox);
ダイアログのタイトルを設定し、「決定」と「取消」と表示され、ButtonData が、各々、OK_DONE と CANCEL_CLOSE である ButtonType を作り、これらを dialogPane に(既に何かあればクリアし)加える。
this.setTitle("色の選択"); ButtonType yesButtonType = new ButtonType("決定", ButtonData.OK_DONE); ButtonType cancelButtonType = new ButtonType("取消", ButtonData.CANCEL_CLOSE); dialogPane.getButtonTypes().setAll(yesButtonType, cancelButtonType);
最後に、次の setResultConverter が重要である。Dialog が知っているのは押された buttonType なので、その後、われわれが必要とする Color を求めそれを返す部分が、call() 関数の中身である。buttonType が null でなければ、buttonType.getButtonData() で ButtonData を求め、それが OK_DONE であれば、dialogFavoriteColorVBox.getColor() でカラーピッカーの色を返す。
setResultConverter(new Callback<ButtonType, Color>() { @Override public Color call(ButtonType buttonType) { ButtonData data = (buttonType == null ? null : buttonType.getButtonData()); return (data == ButtonData.OK_DONE) ? dialogFavoriteColorVBox.getColor() : null; } });
(3)
…についてステージの表示
…についてステージ StageAbout の新しいインスタンスを作り、ここでは、AppName だけを設定し、表示し、応答を待つ。実際のコードは次の通りである。
StageAbout stageAbout = new StageAbout(); stageAbout.setAppName("Alert, Dialog and Stage"); stageAbout.showAndWait();
次に、StageAbout について述べる。StageAbout は下図のような Stage の派生クラスでかつそのコントローラーである。これの Scene Builder における作業図は上述した。Stage(ウィンドウ)は通常、システムメニュー、最小化ボタン、最大化ボタン、閉じるボタンがタイトルバーに表示され、そのアプリケーションが他の Stage(ウィンドウ)を開いている場合、それをアクティブにできる。しかし、いわゆるダイアログボックスは、これらのうち、閉じるボタンがあるだけである。従って、重要な部分は、この変更を行うことである。
まず、appName 等のプロパティの setter を記述する。
public void setAppName(String appName) { this.setTitle(appName); labelAppName.setText(appName); } public void setVerNo(String verNo) { labelVerNo.setText(verNo); } public void setYear(String year) { labelYear.setText(year); } public void setAuthor(String author) { labelAuthor.setText(author); }
次に、コンストラクタ内で、次のことを行う、このアプリケーション内に他の Stage(ウィンドウ)がある場合、それをアクティブにできないように設定し、閉じるボタンのみをタイトルバーに表示する。また、後で利用するために、インスタンス変数 stage に自分自身を保存しておく。
this.initModality(Modality.APPLICATION_MODAL); this.initStyle(StageStyle.UTILITY); this.stage = this;
最後に、関数 initialize() 内で「OK」ボタンが押された時のイベントハンドラで、自分自身を閉じる。
@FXML private void initialize() { buttonOK.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST)); } }); }
この AlertDialogStage の Eclipse プロジェクト src の zip ファイルはこちら、実行ファイルはこちら。