JavaFX
Java bietet mit AWT (Abstract Window Toolkit) und Swing zwei ältere Bibliotheken für grafische Benutzeroberflächen. Der Nachfolger beider ist JavaFX — ein Framework für plattformübergreifende GUIs. Als Framework gibt JavaFX die Architektur vor und steuert den Kontrollfluss nach dem Prinzip der Inversion of Control: Statt dass die Anwendung JavaFX aufruft, registriert sie ihre Funktionen beim Framework, das sie dann aufruft — das sogenannte Hollywood-Prinzip: Don't call us, we'll call you.
Bis Java 11 war JavaFX Bestandteil des JDK, seit Java 11 stellt es ein eigenständiges SDK (Software Development Kit) dar.
Aufbau und Lebenszyklus von JavaFX-Anwendungen
Eine JavaFX-Anwendung besteht aus Bühnen (Stages), die Szenen (Scenes)
enthalten, die wiederum Bildschirmelemente (Nodes) beinhalten. Jede
JavaFX-Anwendung erbt von der Klasse Application, die den Lebenszyklus
verwaltet:
- Die Methode
void launch(args: String[])speichert die Aufrufparameter, erzeugt ein Objekt der eigenen Klasse und ruft die weiteren Lebenszyklus-Methoden auf - Die Methode
void init()kann genutzt werden, um z.B. die Aufrufparameter auszulesen - Die Methode
void start(primaryStage: Stage)bekommt eine Bühne übergeben und wird dazu verwendet, die Bühne zu gestalten und die erste Szene aufzurufen - Die Methode
void stop()wird aufgerufen, bevor der Prozess gestoppt wird und kann genutzt werden, um Aufräumarbeiten durchzuführen
Definition von Szenen
Eine Szene (View) kann objektorientiert im Code oder deklarativ mit FXML-Dokumenten definiert werden. FXML ist eine XML-basierte Beschreibungssprache, die eine klare Trennung zwischen Layout und Code ermöglicht.
JavaFX-spezifische Eigenschaften verknüpfen Szene und Controller:
- Bildschirmelementen können über die Eigenschaft
fx:idIDs zugewiesen werden, über die die Ereignisbehandler-Klasse auf die jeweiligen Elemente zugreifen kann - Die verantwortliche Ereignisbehandler-Klasse wird über die Eigenschaft
fx:controllerfestgelegt - Den zu behandelnden Ereignissen können über entsprechende Eigenschaften wie
z.B.
onActionbei Drucktasten Behandlermethoden der Ereignisbehandler-Klasse zugewiesen werden
- InputView
- OutputView
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" spacing="5.0" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="InputController">
<children>
<TextField fx:id="valueTextField" promptText="Wert" />
<Button text="Zur Ausgabe" onAction="#goToOutput"/>
</children>
<padding>
<Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
</padding>
</VBox>
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" spacing="5.0" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="OutputController">
<children>
<Label fx:id="valueLabel" />
</children>
<padding>
<Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
</padding>
</VBox>
Aufruf von Szenen
Die Methode Parent load(location: URL) der Klasse FXMLLoader lädt ein
FXML-Dokument, erstellt den Szenegraphen und gibt den Wurzelknoten zurück. Der
FXML-Loader instanziiert dabei automatisch den Controller und ruft bei Anzeige
der Szene dessen initialize()-Methode auf.
public class MainClass extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("InputView.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("JavaFX");
primaryStage.setScene(scene);
primaryStage.show();
}
}
Implementierung von Ereignisbehandler-Klassen
In den Controller-Klassen werden die Behandlermethoden implementiert. Jede
Behandlermethode muss einen Parameter vom Typ des Ereignisses besitzen (z.B.
ActionEvent). Die Verknüpfung von Attributen der Controller-Klasse mit
FXML-Elementen erfolgt über die Annotation @FXML und übereinstimmende Namen
zwischen Attributen und fx:id-Werten im FXML-Dokument.
Der Szenenwechsel erfolgt über void setScene(value: Scene) der Klasse
Window.
- InputController
- OutputController
public class InputController implements Initializable {
@FXML
private TextField valueTextField;
private Model model;
@Override
public void initialize(URL location, ResourceBundle resources) {
model = Model.getInstance();
}
@FXML
public void goToOutput(ActionEvent actionEvent) throws IOException {
String value = valueTextField.getText();
model.setValue(value);
Parent root = FXMLLoader.load(getClass().getResource("OutputView.fxml"));
Scene scene = new Scene(root);
Node node = (Node) actionEvent.getSource();
Stage stage = (Stage) node.getScene().getWindow();
stage.setScene(scene);
stage.show();
}
}
public class OutputController implements Initializable {
@FXML
private Label valueLabel;
private Model model;
@Override
public void initialize(URL location, ResourceBundle resources) {
model = Model.getInstance();
String value = model.getValue();
valueLabel.setText(value);
}
}
Die Methode void initialize(location: URL, resources: ResourceBundle) der
Schnittstelle Initializable wird vom FXML-Loader vor Anzeige der dazugehörigen
Szene aufgerufen und ermöglicht es, die Szene dynamisch anzupassen.
Implementierung von Model-Klassen
Model-Klassen verwalten die Anwendungsdaten zentral. Da die Klassen einer JavaFX-Anwendung nur lose gekoppelt sind, sorgt das Entwurfsmuster Singleton dafür, dass zur Laufzeit genau ein Model-Objekt existiert.
public class Model {
private static Model instance;
private String value;
private Model() {}
public static Model getInstance() {
if (instance == null) {
instance = new Model();
}
return instance;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}