Übersetzung des
JavaFX-Tutorials: FXML- und SceneBuilder-Artikel von Vojtech Ruzicka.
So erstellen Sie eine GUI mit JavaFX mithilfe von FXML-Markup und SceneBuilder.
Alle Beiträge in der JavaFX-Reihe:
- JavaFX Tutorial: Erste Schritte
- JavaFX Tutorial: Hallo Welt!
- JavaFX Tutorial: FXML und SceneBuilder
- JavaFX Tutorial: Grundlegende Layouts
- JavaFX Tutorial: Erweiterte Layouts
- JavaFX Tutorial: CSS-Stil
- JavaFX Weaver: Integration von JavaFX- und Spring-Boot-Anwendungen
Traditionelle Art und Weise
Im vorherigen Artikel haben
wir eine einfache Hello World-Anwendung erstellt .
Nur zur Erinnerung - der Code sah so aus:
@Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle("Hello world Application"); primaryStage.setWidth(300); primaryStage.setHeight(200); InputStream iconStream = getClass().getResourceAsStream("/icon.png"); Image image = new Image(iconStream); primaryStage.getIcons().add(image); Label helloWorldLabel = new Label("Hello world!"); helloWorldLabel.setAlignment(Pos.CENTER); Scene primaryScene = new Scene(helloWorldLabel); primaryStage.setScene(primaryScene); primaryStage.show(); }
Wie Sie sehen, wird die gesamte Benutzeroberfläche in Java-Code erstellt.
Dies ist ein sehr einfaches Beispiel. Mit zunehmender Komplexität Ihrer Anwendung kann es jedoch schwierig werden, den resultierenden Code zu verstehen, wenn Sie mehrere Ebenen verschachtelter Layouts und viele Komponenten eingeben müssen. Dies ist jedoch nicht alles - in derselben Klasse gibt es Code, der gleichzeitig für die Struktur, die visuellen Effekte und das Verhalten verantwortlich ist.
Die Klasse hat eindeutig keine einzige Verantwortung. Vergleichen Sie dies zum Beispiel mit dem Webinterface, bei dem jede Seite klar getrennte Aufgaben hat:
- HTML ist eine Struktur
- CSS ist visuelle Effekte
- JavaScript ist Verhalten
Einführung in FXML
Offensichtlich ist es keine gute Idee, den gesamten Code an einem Ort zu haben. Sie müssen es irgendwie strukturieren, damit es einfacher zu verstehen und leichter zu handhaben ist.
In der Realität gibt es dafür viele Entwurfsmuster. In der Regel erhalten Sie die Option "Modellansicht - was auch immer" - etwa "Model View Controller", "Model View Presenter" oder "Model View ViewModel".
Sie können stundenlang über die Vor- und Nachteile verschiedener Optionen diskutieren - lassen Sie es uns hier nicht tun. Noch wichtiger ist, dass Sie mit JavaFx jeden von ihnen verwenden können.
Dies ist möglich, weil Sie zusätzlich zum prozeduralen Design Ihrer Benutzeroberfläche deklaratives XML-Markup verwenden können.
Es stellt sich heraus, dass die hierarchische Struktur von XML eine hervorragende Möglichkeit darstellt, die Hierarchie der Komponenten in der Benutzeroberfläche zu beschreiben. HTML funktioniert ganz gut, oder?
Das JavaFX-spezifische XML-Format heißt FXML. Darin können Sie alle Anwendungskomponenten und ihre Eigenschaften definieren und sie dem Controller zuordnen, der für die Verwaltung der Interaktionen verantwortlich ist.
Laden Sie FXML-Dateien herunter
Wie können wir also unsere Startmethode ändern, um mit FXML zu arbeiten?
FXMLLoader loader = new FXMLLoader(); URL xmlUrl = getClass().getResource("/mainScene.fxml"); loader.setLocation(xmlUrl); Parent root = loader.load(); primaryStage.setScene(new Scene(root)); primaryStage.show();
Hier stellt
root die Root-Komponente Ihrer Benutzeroberfläche dar, andere Komponenten sind darin verschachtelt.
Die
load- Methode hat einen generischen Rückgabewert, sodass Sie einen bestimmten Typ und nicht
Parent angeben können. Als nächstes erhalten Sie Zugriff auf komponentenorientierte Methoden. Dies macht Ihren Code jedoch anfälliger. Wenn Sie den Typ der Root-Komponente in Ihrer FXML ändern, funktioniert die Anwendung möglicherweise zur Laufzeit nicht mehr, aber während der Kompilierung treten keine Fehler auf. Dies liegt daran, dass es jetzt einen Konflikt des Typs gibt, der in Ihrer FXML und im Java FXML-Loader deklariert ist.
Erstellen Sie eine FXML-Datei
Jetzt wissen wir, wie die FXML-Datei geladen wird, müssen sie jedoch noch erstellen. Die Datei muss die Erweiterung .fxml haben. In einem Maven-Projekt können Sie diese Datei in den Ressourcenordner stellen oder FXMLLoader kann sie von einer externen URL herunterladen.
Geben Sie nach dem Erstellen der Datei die XML-Deklaration in der ersten Zeile ein:
<?xml version="1.0" encoding="UTF-8"?>
Importieren
Bevor Sie einer Datei einzelne Komponenten hinzufügen, müssen Sie sicherstellen, dass diese korrekt erkannt werden. Fügen Sie dazu Importanweisungen hinzu. Dies ist dem Importieren in Java-Klassen sehr ähnlich. Sie können wie gewohnt einzelne Klassen importieren oder Platzhalter verwenden. Schauen wir uns ein Beispiel für einen Importabschnitt an:
<?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?>
Die gute Nachricht ist, dass Ihre IDE Ihnen beim Hinzufügen des Imports helfen sollte, anstatt alle Importanweisungen manuell hinzuzufügen, wie beim Hinzufügen zu Java-Klassen.
Komponenten hinzufügen
Jetzt ist es Zeit, einige Komponenten hinzuzufügen. In einem
früheren Artikel haben wir erfahren, dass jede Szene nur eine untergeordnete Komponente haben kann. Fügen Sie zunächst ein einfaches Label hinzu:
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <Label>Hello World!</Label>
Natürlich ist die Kennzeichnung als Wurzelkomponente kein sehr realistisches Beispiel. Es ist normalerweise vorzuziehen, eine Art Layout (Layout) zu verwenden, das ein Container für mehrere Komponenten ist und deren Anordnung organisiert. Wir werden uns später in dieser Serie mit Layouts befassen, aber jetzt verwenden wir einfach eine einfache VBox, die ihre untergeordneten Elemente vertikal übereinander platziert.
<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Label?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.control.Button?> <VBox> <Label text="Hello world!"/> <Label text="This is a simple demo application."/> <Button text="Click me!"/> </VBox>
FX Namespace
Es gibt einige FXML-Elemente und -Attribute, die standardmäßig nicht verfügbar sind. Sie müssen einen Namespace FXML hinzufügen, um sie verfügbar zu machen. Es muss zur Root-Komponente hinzugefügt werden:
<?xml version="1.0" encoding="UTF-8"?> ... <VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"> ... </VBox>
Jetzt können Sie die neuen Elemente aus dem FX-Namespace verwenden. Versuchen wir, unseren Komponenten eindeutige Bezeichner hinzuzufügen:
<Label fx:id="mainTitle" text="Hello world!"/>
Das Attribut
fx: id ist eine eindeutige Kennung für eine Komponente, mit der auf eine Komponente aus anderen Teilen unserer FXML und sogar aus unserem Controller verwiesen werden kann.
Skripte
Unsere Bewerbung ist noch statisch. Es gibt mehrere Beschriftungen und eine Schaltfläche, aber die Anwendung führt keine dynamischen Aktionen aus.
Lassen Sie uns auf das Klicken auf unsere Schaltfläche reagieren und den Titel von "Click me!" In "Click me again!" Ändern.
Als Erstes fügen Sie einen onAction-Ereignishandler für unsere Schaltfläche hinzu.
<Button fx:id="mainButton" text="Click me!" onAction="buttonClicked()"/>
Achten Sie auf fx: id. Dies ist die Kennung, die später verwendet wird, um auf die Schaltfläche zu verweisen.
Jetzt müssen Sie eine Funktion bereitstellen, die aufgerufen wird, um das Ereignis zu behandeln. Es kann innerhalb des Tags fx: script definiert werden. Wichtig ist, dass Sie verschiedene Sprachen verwenden können, um ein Skript, JavaScript, Groovy oder Clojure zu schreiben. Schauen wir uns ein Beispiel in JavaScript an:

Beachten Sie, dass wir auf unsere Button-Komponente mit dem mainButton-Bezeichner verweisen, der folgendermaßen deklariert wurde:
fx:id = "mainButton"
Sie müssen auch angeben, welche Skriptsprache Sie in der FXML-Datei verwenden:
<?language javascript?>
Schauen wir uns den vollständigen Text des Beispiels an:

Soll ich das benutzen?
Das obige Beispiel zeigt, wie Komponenten mit
fx: id referenziert werden und wie einfaches Verhalten mit einem JavaScript-Skript hinzugefügt wird. Solltest du das wirklich tun?
Die Antwort lautet in den meisten Fällen nein. Bei diesem Ansatz gibt es mehrere Probleme. Der Grund, warum FXML eingeführt wurde, war eine Trennung der Interessen, um die Struktur und das Verhalten der Benutzeroberfläche zu trennen. In diesem Skript wurde das mit der Benutzeroberflächenstruktur zusammengeführte Verhalten erneut zurückgegeben. Da wir nicht mehr mit Java-Code, sondern mit XML arbeiten, gingen außerdem alle Codeprüfungen zur Kompilierungszeit und die Typensicherheit verloren. Jetzt werden alle Probleme in der Anwendung zur Laufzeit und nicht zur Kompilierungszeit erkannt. Die Anwendung ist sehr zerbrechlich und fehleranfällig geworden.
Controller hinzufügen
Was kann also getan werden, um eine klare Interessentrennung zu erreichen? Sie können den Controller mit unserer FXML-Datei verknüpfen. Ein Controller ist eine Java-Klasse, die für das Verhalten und die Benutzerinteraktion in einer Anwendung verantwortlich ist. Auf diese Weise können Sie Typensicherheits- und Kompilierungszeitprüfungen zurückgeben.
Der Controller ist ein POJO, er sollte nichts erweitern oder implementieren und keine speziellen Anmerkungen enthalten.
Wie kann ich eine Controller-Klasse mit unserer FXML verknüpfen? Grundsätzlich gibt es zwei Möglichkeiten.
In Java
Sie können eine Instanz des Controllers selbst erstellen oder andere Methoden zum Erstellen einer Instanz verwenden, z. B. die Abhängigkeitsinjektion. Dann laden Sie einfach Ihren
FXMLLoader herunter.
FXMLLoader loader = new FXMLLoader(); loader.setController(new MainSceneController());
In FXML
Sie können die Klasse Ihres Controllers als Attribut
fx: controller angeben, das sich in der Root-Komponente befinden soll.
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> ... </VBox>
Wenn Sie Ihre Controller-Klasse in FXML deklarieren, wird sie automatisch für Sie erstellt. Dieser Ansatz hat eine Einschränkung: Im Controller müssen Sie einen Konstruktor ohne Argumente erstellen, um das Erstellen einer neuen Instanz der Controller-Klasse zu vereinfachen.
Um auf eine automatisch erstellte Instanz eines Controllers zuzugreifen, können Sie den FXML-Loader verwenden:
FXMLLoader loader = new FXMLLoader(); loader.setLocation(getClass().getResource("/mainScene.fxml")); MainSceneController controller = loader.getController();
Aufrufen von Controller-Methoden
Nachdem Sie eine Steuerung haben, können Sie das Skript löschen und die Logik zum Drücken von Schaltflächen direkt in der Steuerung implementieren:
public class MainSceneController { public void buttonClicked() { System.out.println("Button clicked!"); } }
Der nächste Schritt besteht darin, den Aufruf dieser Methode als
onAction- Ereignishandler unserer Schaltfläche zu registrieren. Um auf Methoden von unserem Controller zu verweisen, müssen wir das
# -Zeichen vor dem Methodennamen verwenden:
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" fx:controller="com.vojtechruzicka.MainSceneController"> <Label fx:id="mainTitle" text="Hello world!"/> <Label fx:id="subTitle" text="This is a simple demo application."/> <Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/> </VBox>
Wenn auf eine Schaltfläche geklickt wird, wird die
MainSceneController.buttonClicked () -Methode
aufgerufen . Beachten Sie, dass dies nur funktioniert, wenn die Methode als öffentlich deklariert ist. Wenn der Zugriffsmodifikator strenger ist, müssen Sie die Methode mit der Anmerkung
@FXML versehen .
@FXML private void buttonClicked() { System.out.println("Button clicked!"); }
Einbetten von Komponenten in eine Steuerung
Bisher drucken wir nur auf die Konsole. Was ist, wenn wir den Text unserer Schaltfläche erneut in "
Klick mich noch einmal " ändern möchten? Wie können wir Links zu Komponenten in unserer Steuerung erhalten?
Zum Glück ist das einfach.
Erinnerst du dich an diese
fx: id Attribute?
<Button fx:id="mainButton" text="Click me!" onAction="#buttonClicked"/>
JavaFX versucht, Komponenten mit
fx: id automatisch mit den Feldern abzugleichen, die in Ihrem Controller mit demselben Namen definiert sind.
Angenommen, wir haben einen oben beschriebenen Knopf mit
fx:id="mainButton"
JavaFX versucht, das Schaltflächenobjekt in einem Feld namens
mainButton in Ihren Controller
einzufügen :
public class MainSceneController {
Wie bei den vorherigen Methoden müssen Ihre Felder
öffentlich oder mit
@FXML- Anmerkungen versehen
sein .
Jetzt, da wir einen Link zu unserer Schaltfläche haben, können wir ihren Text leicht ändern:
public class MainSceneController { @FXML private Button mainButton; @FXML private void buttonClicked() { mainButton.setText("Click me again!"); } }
Szenenbildner
Das Schreiben Ihrer GUI-Struktur in XML ist möglicherweise natürlicher als in Java (insbesondere, wenn Sie mit HTML vertraut sind). Es ist jedoch immer noch nicht sehr praktisch. Die gute Nachricht ist, dass es ein offizielles Tool namens Scene Builder gibt, das Sie bei der Erstellung der Benutzeroberfläche unterstützt. Kurz gesagt, dies ist ein grafischer Editor für Ihre GUI.

Der Editor hat drei Hauptbereiche:
- Der linke Teil zeigt die verfügbaren Komponenten an, die in den mittleren Teil gezogen werden können. Es enthält auch eine Hierarchie aller Komponenten in Ihrer Benutzeroberfläche, sodass Sie leicht darin navigieren können.
- Der mittlere Teil ist Ihre Anwendung, die basierend auf Ihrer FXML-Datei angezeigt wird.
- Rechts befindet sich der aktuelle Komponenteninspektor. Hier können Sie verschiedene Eigenschaften der ausgewählten aktuellen Komponente bearbeiten. Jede in der Mitte der Hierarchie ausgewählte Komponente wird im Inspektor angezeigt.
Standalone
Scene Builder kann als eigenständige Anwendung
heruntergeladen werden, mit der FXML-Dateien bearbeitet werden können.
Integration mit IntelliJ IDEA
Alternativ bietet Scene Builder die Integration in die IDE an.
In IntelliJ IDEA können Sie mit der rechten Maustaste auf eine beliebige FXML-Datei klicken und dann in SceneBuilder die Menüoption Öffnen auswählen.
Alternativ integriert IntelliJ IDEA SceneBuilder direkt in die IDE. Wenn Sie die FXML-Datei in IDEA öffnen, werden am unteren Bildschirmrand zwei Registerkarten angezeigt
Für jede FXML-Datei können Sie einfach zwischen der direkten oder der direkten Bearbeitung der FXML-Datei über SceneBuilder wechseln.

In IntelliJ IDEA können Sie den Speicherort der SceneBuilder-Programmdatei konfigurieren:
Settings → Languages & Frameworks → JavaFX → Path to SceneBuilder
Was weiter
Im nächsten Beitrag unserer Serie werden einige grundlegende Layouts erläutert, mit denen die Komponenten einer GUI-Anwendung in JavaFX organisiert werden können.