JavaFX enthält ab Version 8 auch Dialogklassen. Dazu gehören die vorgefertigten Standarddialoge Alert (Warnung), TextInputDialog (Texteingabe) und ChoiceDialog (Auswahl). Über deren Verwendung ist im Internet auch einiges zu finden. Das Erstellen eigener Dialoge wird dagegen kaum beschrieben. Am Beispiel eines Login-Dialogs soll dieser Blog das ändern. Der Beispielcode erstellt die Dialogoberfläche einmal programmatisch und einmal per FXML. Der Beispielcode ist auf Github zu finden.
Das Basisvorgehen in JavaFX
Für eigene Dialoge bietet sich ein Blick auf die Struktur der vorgefertigten Dialoge an

Am einfachsten leitet sich der eigene Dialog – wie bei den vorhandenen Dialogen – von der Klasse javafx.scene.control.Dialog<R> ab. <R> ist der Rückgabetyp. Für die Darstellung der Oberfläche inklusive der Buttons ist die DialogPane-Eigenschaft des eigenen Dialogs zu setzen oder zu bestücken. Abschließend benötigt der eigene Dialog noch einen Konverter, um die relvanten Daten aus den Eingabefeldern in ein Rückgabeobjekt zu transferieren.
Der Aufruf einer eigenen Dialogklasse ist dann z.B.
CodedLoginDialog codedDialog = new CodedLoginDialog();
Optional result = codedDialog.showAndWait();
result.ifPresent(login -> System.out.println(login));
Bei Abbruch des Dialogs enthält das Optional keinen Wert, die Ausgabe findet entsprechend nicht statt. Die Javadoc zur Dialog-Klasse zeigt weitere Aufrufvarianten.
Erstellen der Dialogoberfläche
DieDialogPane
eines Dialogs bestimmt das Aussehen.

Das wichtigste dürfte der Content sein. Im Bild ist ihm eine Grid mit Labeln und Eingabefeldern eines Logins zugewiesen.
Diverse optionale Ergänzungen sind möglich:
- Zusätzlich zum Titel des Dialogs ist noch ein Headertext möglich.
- Ein Bild/Icon (
graphic
-Eigenschaft) ist im Bild nicht dargestellt. Dessen Position hängt vom Headertext ab. Ist ein Headertext angegeben, erscheint ein Bild rechts vom Headertext, andernfalls links vom Content. - Ein expandierbarer Content sorgt wie im Bild für einen Button, der auf Anforderung den Inhalt des expandable Contents anzeigt.
Programmatische Oberfläche
In der eigenen Dialogklasse ist der Content der DialogPane
zu befüllen
this.grid = new GridPane();
...
final DialogPane dialogPane = getDialogPane();
dialogPane.setContent(grid);
...
FXML-Oberfläche
Seit der Version 8 des SceneBuilder bietet dieser die Möglichkeit eine DialogPane
grafisch zu erstellen und als FXML-File zu speichern.
Mit Hilfe des FXMLLoader erhält eigenen Dialog die gewünsche DialogPane
:
DialogPane dialogPane =
(DialogPane)FXMLLoader.load(
getClass().getResource("/fxml/DialogPane.fxml"));
...
setDialogPane(dialogPane);
Buttons ergänzen
Die Buttons eines Dialoges speichert die DialogPane
in der Eigenschaft Buttontypes. Es gibt diverse vordefinierte Buttons, die der Buttontype-Collection hinzugefügt werden können. Im Beispiel enthält der Dialog einen Abbruch und einen Ok-Button:
dialogPane.getButtonTypes().addAll(ButtonType.CANCEL, ButtonType.OK);
Obwohl in der DialogPane
verborgen, sind die vordefinierten Button im Nachgang noch anpassbar, z.B. der Text des Ok-Buttons
Button okButton = (Button)dialogPane.lookupButton(ButtonType.OK);
okButton.setText("Login");
Der Konverter
Nach dem Ok soll der Dialog nun auch die Eingaben verpackt in der Ergebnisklasse zurück liefern. Das ist Aufgabe des Konverters, der in eigenen Klassen entsprechend zu definieren ist
setResultConverter((dialogButton) -> {
ButtonData data =
dialogButton == null ? null : dialogButton.getButtonData();
return data == ButtonData.OK_DONE
? new Login(usernameField.getText(), passwordField.getText())
: null;
});
Ist im Beispiel der Dialog mit dem Ok-Button beendet worden, so legt das Lambda einen neues Ergebnisobjekt an und übergibt dem Konstruktur die Daten aus den Eingabefeldern.
Für die Konvertierung benötigt das Lambda referenzen auf die Eingabefelder. Bei der programmatischen Erstellung der Oberfläche sind diese schnell als Attribute der neuen Dialog-Klasse deklariert. Anders verhält es sich, wenn die Oberfläche per FXMLLoader erstellt ist. In diesem Fall muss die Dialogklasse die Referenzen im FXML-Objektbaum suchen. Die gewünschten Elemente können im FXML per Id gekennzeichnet werden
<TextField fx:id="usernameField"...
um anschließend nach dem Laden im Elementbaum der DialogPane gesucht zu werden
final TextField usernameField =
(TextField)dialogPane.lookup("#usernameField");
Validierung
Die Eingaben eines Dialogs sollten natürlich nur weiter gegeben werden, wenn sie sinnvoll sind. Über Data-Binding könnten nun Hinweise, Markierungen, etc. in den Content eingeblendet werden. Aber auch der Ok-Button sollte nur bei vollständigen Eingaben überhaupt aktiv sein. In Beispiel muss mindestens ein User angegeben sein, bevor ein Login möglich ist.
Zuvor wurde der Text des Ok-Buttons angepasst. Aber auch die übrigen Buttoneigenschaften sind ansprechbar
okButton.setDisable(true);
usernameField.textProperty().addListener(
(event, oldValue, newValue)
-> okButton.setDisable(newValue.trim().isEmpty()));
Fazit
Solange das vorgegebene Layout der für die eigenen Zwecke ausreicht, sind eigene Dialoge seit Java 8 recht einfach zu erstellen.