Lektion 1: Die Grundstruktur eines JavaFX-Programms
Projekt in NetBeans anlegen
- Datei -> Neues Projekt -> JavaFX -> JavaFX FXML Application -> Weiter
- ProjectName -> Gewicht -> FXML name -> GewichtsView -> Create Application Class de.erichweigand.gewicht.Gewicht -> Fertigstellen
- F6 (Run Project)
Es erscheint ein Fenster mit einem Button, der die Aufschrift Click Me! trägt. Wird der Button geklickt, erscheint der Schriftzug Hello World unterhalb des Buttons und der Text You clicked me! wird auf der Konsole ausgegeben.
Code-Analyse
Es werden 3 Dateien generiert, um dieses Programm zu realisieren.
- GewichtsView.fxml Die Beschreibung der Oberfläche in FXML. Diese Datei wird nur selten in NetBeans geöffnet. Ein Doppelklick auf die Datei öffnet den SceneBuilder, um die Oberfläche mittels Drag und Drop bearbeiten zu können. Um den XML-Code der Datei zu bearbeiten die Datei anklicken, rechte Maustaste und dann Edit auswählen.
- GewichtsViewController.java Der Java-Code für die Oberfläche, die in der FXML-Datei definiert ist.
- Gewicht.java Die Hauptklasse der Applikation
Die Hauptdatei
Die Hauptklasse einer JavaFX Anwendung erbt von Klasse Application. In der main Methode dieser Klasse wird die JavaFX Umgebung mit der Funktion launch gestartet. Um die Oberfläche aufzubauen, muss die Methode start überschrieben werden. Dieser Methode wird eine Stage (zu deutsch Bühne) mitgegeben, auf der die Applikation spielt. Die Stage ist das Hauptfenster, das mit der Applikation gefüllt wird. Die Oberfläche wird durch die FXML-Datei definiert. Deswegen wird diese mit einem FXMLLoader geladen und einer Scene übergeben (Es wird also eine Bühnenszene gebaut) Die Szene wird der Bühne übergeben und der Vorhang wird geöffnet (stage.show). Da die Definition der Oberfläche in der FXML Datei ist und der Java-Code zur Oberfläche im Controller ist, wird die Hauptklasse nur selten noch verändert.
package de.erichweigand.gewicht; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; /** * * @author erich */ public class Gewicht extends Application { @Override public void start(Stage stage) throws Exception { Parent root = FXMLLoader.load(getClass().getResource("GewichtsView.fxml")); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); } /** * @param args the command line arguments */ public static void main(String[] args) { launch(args); } }
Der Controller
Der Controller enthält den Java-Code, der der Oberfläche Leben einhaucht. Mit der Annotation @FXML wird eine Verbindung zur FXML-Datei hergestellt. So wird mit dieser Annotation einmal die Verbindung zu einem Label hergestellt (der Wert von fx:id in der FXML-Datei ergibt den Namen der Java-Variablen) und zum zweiten die Funktion, mit der auf ein Button-Klick reagiert werden soll (der Wert von onAction in der FXML-Datei gibt den Methodennamen an).
package de.erichweigand.gewicht; import java.net.URL; import java.util.ResourceBundle; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Label; /** * * @author erich */ public class GewichtsViewController implements Initializable { @FXML private Label label; @FXML private void handleButtonAction(ActionEvent event) { System.out.println("You clicked me!"); label.setText("Hello World!"); } @Override public void initialize(URL url, ResourceBundle rb) { // TODO } }
Programmstruktur erweitern
Wenn man größere Programme schreibt, sollte man darauf achten eine übersichtliche Struktur durchzuhalten. Wie die richtige Struktur aussieht ist teils Tradition teils Geschmack des Programmierers. Ich benutze hier diese Struktur:
Im Main-Package ist nur die Hauptdatei. Die anderen Programmteile sind in Unterpackages, die je nach Komplexität des Programms weitere Unterpackages enthalten können.
- controller Hier sind die Controller-Klassen (also die Verwaltung der Oberflächen, wie zum Beispiel Behandlung Button-Klicks oder Eingaben in Textfelder)
- model Hier sind die Klassen, die die Datenstruktur der Applikation enthalten (die meist die Struktur der Datenbank in Java-Klassen abbilden)
- resource Hier hinein kommen alle Materialien, die kein Java-Code sind, aber vom Programm benötigt werden (z.B. Icons, Hintergrundbilder, …)
- service Hier hinein kommen alle Hilfsklassen, die in mehreren Controllern gebraucht werden, oder auch nicht direkt mit der Abarbeitung von Oberflächen-Ereignissen zu tun haben.
- view Hier hinein kommen alle Dateien, die die Oberfläche definieren. Also vor allem die FXML Dateien, aber auch Java-Klassen, die Oberflächen erzeugen.
Verschiebt man die Dateien in NetBeans übernimmt NetBeans das Umschreiben (Refactoring) der Java-Klassen auf die neue Package-Struktur. In 2 Fällen muss man hier allerdings von Hand eingreifen, um wieder ein lauffähiges Programm zu erhalten
- Datei Gewicht.java: Hier muss der Pfad zur FXML-Datei umgesetzt werden. Ich benutze hier den absoluten Pfad zur Datei.
Parent root = FXMLLoader.load(getClass().getResource("/de/erichweigand/gewicht/view/GewichtsView.fxml"));
- Datei GewichtsView.fxml: In der FXML-Datei steht, welcher Controller die Abarbeitung der hier definierten Oberfläche übernimmt. Dieser Controller-Name hat sich durch das Refactoring geändert.
fx:controller="de.erichweigand.gewicht.controller.GewichtsViewController"
Oberfläche erweitern
Wo wir gerade bei größeren Programmen sind. Jede Desktop-Applikation die was auf sich hält hat in etwa folgende Struktur.
- In der Mitte ein Hauptbereich.
- Oben eine Menüleiste
- Unten eine Statusleiste für Kurzmeldungen
- Unter Umständen links und/oder rechts eine Navigationsleiste
Diese Struktur wird in JavaFX durch das Layout BorderPane erreicht. Auf dieses Layout stellen wir diese Applikation nun um, spendieren der Applikation auch noch gleich eine Menüleiste. Der Inhalt der Menüleiste beschränkt sich fürs erste auf File -> Exit. Die Funktionalität dafür wird im Controller umgesetzt. Doch zuerst die sich ergebende FXML Datei.
<?xml version="1.0" encoding="UTF-8"?> <?import java.lang.*?> <?import java.util.*?> <?import javafx.scene.*?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <BorderPane prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="de.erichweigand.gewicht.controller.GewichtsViewController"> <center> <Button fx:id="button" onAction="#handleButtonAction" text="Click Me!" /> </center> <top> <MenuBar BorderPane.alignment="CENTER"> <menus> <Menu mnemonicParsing="false" text="File"> <items> <MenuItem mnemonicParsing="false" onAction="#exitApplication" text="Exit" /> </items> </Menu> </menus> </MenuBar> </top> <bottom> <Label fx:id="label" minHeight="20" minWidth="80" BorderPane.alignment="CENTER" /> </bottom> </BorderPane>
Damit das Exit über die Menüleiste funktioniert benötigt der GewichtsViewController noch eine Funktion, die die Applikation beendet.
@FXML private void exitApplication(ActionEvent event) { System.exit(0); }
Abgesehen von der Menüleiste funktioniert die Applikation nach dem Neukompilieren noch genau so wie vorher. Die kompletten Source-Dateien, die sich nach Lektion 1 ergeben befinden sich im Anhang an diese Seite.