Zum Hauptinhalt springen

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.

info

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:id IDs zugewiesen werden, über die die Ereignisbehandler-Klasse auf die jeweiligen Elemente zugreifen kann
  • Die verantwortliche Ereignisbehandler-Klasse wird über die Eigenschaft fx:controller festgelegt
  • Den zu behandelnden Ereignissen können über entsprechende Eigenschaften wie z.B. onAction bei Drucktasten Behandlermethoden der Ereignisbehandler-Klasse zugewiesen werden
InputView.fxml
<?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>

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.

MainClass.java
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.java
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();
}

}
info

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.

Model.java
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;
}

}