JavaFX FXML und SceneBuilder in IntelliJ
GUI-Erstellungswerkzeug
in Arbeit ...
1. Überblick
Ziel dieses Dokuments ist es, die grundlegenden Ansätze zur Erstellung einer JavaFX-Applikation unter Verwendung der deklarativen GUI-Definitionssprache FXML (ein XML-Format) zu erläutern und zu zeigen (hauptsächlich mit dem visuellen GUI-Erstellungs-Tool SceneBuilder) und etwas zu erläutern.
Infos zu Installation von Java, JavaFX, IntelliJ und SceneBuilder finden sich unter
TODO: in Arbeit
Eine vollständige Dokumentation zu FXML findet sich z.B unter Introduction to FXML | openjfx.io: JavaFX 19.
Anbei ein lauffähiges, sehr einfaches Maven-Projekt mit FXML:
Hier ein weiteres Mavan-Projekt, das die Verwendung mehrerer, dynamisch geladener FXML-Dateien zeigt:
2. Installation und Konfiguration von SceneBuilder
Download-URL: Scene Builder - Gluon
Für Interessierte steht der Source-Code unter GitHub - gluonhq/scenebuilder: Scene Builder is a visual, drag 'n' drop, layout tool for designing JavaFX applications zur Verfügung.
Version 20 ist auch mit älteren Java-Versionen (z.B. JDK-17) kompatibel!
Im folgenden wird vorausgesetzt, dass das IntelliJ-JavaFX-Plugin aktiv ist
In IntelliJ ist unter Menü C:\Users\WerAuchImmer\AppData\Local\SceneBuilder\SceneBuilder.exe
).
Auch ohne diese Konfiguration ist eine (allerdings etwas veraltete) embedded Version von SceneBuilder verfügbar. Diese erreicht man bei Öffnen einer FXML-Datei in IntelliJ als zweiter Karteireiter unten am Editor-Fenster (Text / Scene Builder).
3. Verwendung SceneBuilder
Damit ein UI-Element vom Controller aus vernünftig erreichbar ist, muss dem Element eine fx:id
zugeordnet werden (rechts unten im Bereich Code
). Diese ID wird zur Definition eines gleichnamigen Controller-Attributes (mit Annotation @FXML
) verwendet.
Damit die Kooperation mit der Controller-Klasse funktioniert, muss dessen voll qualifizierte Klassenname angegeben werden (links unten im Bereich Controller
)
Event-Handler-Methoden-Namen für die gewünschten Event-Arten sind ebenfalls unter Code
festzulegen.
Nach Erzeugen der gewünschten UI-Elemente-Hierarchie und Festlegung der Controller-Klasse, aller gewünschten fx:id`s und Event-Handler-Methoden kann ein "Gerippe" der Controller-Klasse erstellt werden mit (ganz unten!):
,
.
Im Formaular sollten beide Checkboxen unten (`CommentsFull
) angeklickt werden, um maximale Funktionalität und Kommentare zu erhalten.
4. FXML-Datei
Diese kann entweder im SceneBuilder oder innerhalb von IntelliJ erzeugt werden. Die Endung MUSS *.fxml
sein, wobei dies in beiden Erzeugungswegen automatisch erfolgt bzw. vorgeschlagen wird. Sie sollte gemäß der empfohlenen Maven-Projekt-Struktur unter resources
liegen. Empfehlenswert ist z.B. der Pfad des Projekt-Basispackage und darunter ein Ordner 'view'.
Benennung erfolgt häufig im Stil von StyleSheets: Ein korrespondierender Controller-Name MeinUiTeilController.java
würde zu mein-ui-teil.fxml
führen.
<?xml version="1.0" encoding="UTF-8"?> (1)
<?import javafx.scene.control.Button?> (2)
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane id="id_base-borderpane" fx:id="fxmlParent" minHeight="200.0" minWidth="300.0"
xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="my.demo.view_ctlr.BaseController"> (3)
<center> (4)
<VBox alignment="CENTER" BorderPane.alignment="CENTER"> (5)
<children> (6)
<Button fx:id="btnDrueckenFuerMenue" mnemonicParsing="false"
onAction="#menueZeigenAction" text="Drücken für Menü" /> (7)
<Label prefHeight="62.0" prefWidth="192.0"
text="Menü 'Settings / Inhaltsbereich aktivieren' wählen!"
textAlignment="CENTER" wrapText="true" /> (8)
</children>
</VBox>
</center>
</BorderPane>
1 | XML Definitionen |
2 | Import analog zu Java-Import |
3 | XML-Root-Element – erbt üblicherweise von javafx.scene.Node .
Darin muss als Attribut fx:controller die zuständige Controller-Klasse voll qualifiziert mit hier
fx:controller="my.demo.view_ctlr.BaseController" festgelegt sein. Im folgenden Beispiel wird für
jede FXML-Datei eine fx:id="fxmlParent" der Wurzel-Pane erzwungen, wodurch elegantere und vereinheitlichte Einbindung der FXML-UIs möglich wird. |
4 | Properties der Klasse sind als Sub-Elemente in lowercase vorhanden, wenn nicht null (leer)
(für BorderPane : center , top , bottom , left , right ) |
5 | In diesen Sub-Elementen können weitere Pane - und Control -Elemente belidbig tief
verschachtelt auftreten |
6 | children -Property der VBox |
7 | Darin weitere Sub-Elemente enthalten (hier 'Button') |
8 | und hier Label |
Die FXML-Struktur ist also recht plausibel und mit etwas Übung auch recht gut manuell editierbar.
Dennoch ist das GUI-Erstellungstool SceneBuilder eine große Hilfe.
Dieser ist beispielsweise fähig, ein Gerippe für den dazugehörigen Controller zu erstellen:
/**
* Sample Skeleton for 'demo1.fxml' Controller Class
*/
package my.demo.view_ctlr;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
public class BaseController {
@FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
@FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
@FXML // fx:id="btnDrueckenFuerMenue"
private Button btnDrueckenFuerMenue; // Value injected by FXMLLoader
@FXML // fx:id="fxmlParent"
private BorderPane fxmlParent; // Value injected by FXMLLoader
@FXML
void menueZeigenAction(ActionEvent event) {
}
@FXML // This method is called by the FXMLLoader when initialization is complete
void initialize() {
assert btnDrueckenFuerMenue != null : "fx:id=\"btnDrueckenFuerMenue\" was not injected: check your FXML file 'demo1.fxml'.";
assert fxmlParent != null : "fx:id=\"fxmlParent\" was not injected: check your FXML file 'demo1.fxml'.";
}
}
Alle mit der Annotation gekennzeichneten Attribute und Methoden sind mit den korrespondierenden Elementen (bei UI-Elementen wie Panes, Controls, etc. über die fx:id
, die Event-Handler-Methoden und der Controller über ihren Namen) verbunden.
5. Beispiel-Applikation
Schon die obige, erläuterte FXML-Datei Base.fxml
ist Teil der App. Die restlichen Dateien sind nachfolgend vollständig dargestellt.
Das Eclipse-Projekt ist hier zum Download verfügbar
5.1. Datei-Struktur dess Eclipse-Projekts VERALTET !!
${eclipse-project-dir} + src + my + demo + view + Base.fxml + ContentArea.fxml + MenuBarArea.fxml + MyDemoApp.fxml + view_ctlr + AccessToAll.java + BaseController.java + ContentAreaController.java + MenuBarAreaController.java + MyDemoApp.java + FxmlControllerIface.java + my-demo-app.css
Nun folgen die einzelnen Klassen, FXML-Dateien und die CSS-Datei Hier folgt nun die Applikations-Klasse zum Start der App (im wesentlichen in Erstnutzungs-Reihenfolge):
-
Base.fxml
… vorweg zur Erläuterung der FXML-Dateien verwendet. Definiert das "Begrüßungs-UI" der Applikation -
FxmlApp.java
… Klasse mit Main-Methode, startet die App -
AccessToAll.java
… zentralisiert Zugriffsmöglichkeit auf die Controller-Klassen, die jeweils Referenzen zu allen benötigten UI-Elementen innerhalb ihrer Root-Pane (FXML-Root-Element) bereitstellen -
FxmlControllerIface.java
… Interface, das eine vereinheitlichte Handhabung der Controller-Klassen ermöglicht. Alle Controller implementieren es. -
BaseController.java
… Controller des später modifizierten "Begrüßungs-UI" (Button-Event-Methode undfx:id
der äußersten, unverändert bleibenden Root-BorderPane
, deren 5 Bereiche später modifiziert werden) -
MenuBarArea.fxml
… Enthält eineHBox
und darin den hinzuzfügendenMenuBar
-
MenuBarAreaController.java
… der für den gesamtenMenuBar
zuständige Controller -
ContentArea.fxml
… die mittels Menü Settings → Inhaltsbereich aktivieren den Bereichcenter
ersetzendeVBox
mit derTabPane
-
ContentAreaController.java
… der Controller dazu -
my-demo-fxmlapp.css
5.2. Klasse "my.demo.FxmlApp"
package my.demo;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import my.demo.view_ctlr.AccessToAll;
import my.demo.view_ctlr.BaseController;
public class FxmlApp extends Application {
public FxmlApp() {
}
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
//Schlecht: (kein Zugriff auf Controller):
// Parent root = loader.load(getClass().getResource("MyApp.fxml"));
// besser - ermöglicht Zugriff auf Controller und damit UI-Elemente:
// FXMLLoader loader = new FXMLLoader(getClass().getResource(FxmlApp.BASE_FXML));
// Parent root = loader.load();
// MUSS NACH loader.load() erfolgen - sonst wird 'null' !!!!! zurückgeliefert:
// BaseController baseController = loader.<BaseController>getController();
// Am besten: Alle Controller implementieren Interface FxmlControllerIface:
// Damit ist es möglich, eine Methode 'createFromFxml(...)' zu implementieren, die
// das Laden und Konfigurieren einheitlich erledigt.
// Die Generic-Parameter (hier <BorderPane, BaseController>) werden nur für den
// Rückgabetyp benötigt und können weggelassen werden, da der Compiler den erforderlichen
// Rückgabetyp bei der Zuweisung ohnehin prüfen kann/muss.
BaseController baseCtlr = AccessToAll.singleton()
.<BorderPane, BaseController>createFromFxml(AccessToAll.BASE_FXML);
Parent root = baseCtlr.getFxmlParent();
Scene scene = new Scene(root);
AccessToAll.singleton().setScene(scene);
AccessToAll.singleton().setBaseController(baseCtlr);
scene.getStylesheets().add(getClass().getResource("my-demo-fxmlapp.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
}
Ist die Application-Klasse mit der main
-Methode
5.3. Klasse "my.demo.view_ctlr.AccessToAll"
Klasse AccessToAll
ermöglicht allen Controllern und weiteren Programmteilen, auf die UI-Elemente der anderen Controller zuzugreifen – z.B. kann der MenuBarAreaController
auf Elemente des ContentArea
zugreifen. Auf diese Weise kann jede Klasse, die Zugriff auf dieses (Singleton-) Objekt hat, alle per jeweils eigener FXML-Datei organisierten und in AccessToAll
aufgenommenen Komponenten oder Funktionalitäten erreichen.
package my.demo.view_ctlr;
import java.io.IOException;
import javafx.event.Event;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.layout.Pane;
import my.demo.FxmlControllerIface;
public class AccessToAll { // könnte mann auch 'AppContext' o.ä. nennen
public static final String BASE_FXML = "/my/demo/view/Base.fxml";
public static final String MENUBAR_AREA_FXML = "/my/demo/view/MenuBarArea.fxml";
public static final String CONTENT_AREA_FXML = "/my/demo/view/ContentArea.fxml";
private static AccessToAll instance;
private Scene scene;
private BaseController baseController;
private MenuBarAreaController menuBarAreaController;
private ContentAreaController contentAreaController;
public static AccessToAll singleton() { // immer SELBES Objekt zurückliefern
if (instance == null) {
instance = new AccessToAll(); // kann nur ein einziges Mal instanziert werden
}
return instance;
}
private AccessToAll() { // Erzeugung Singleton: Verhindern, dass mit 'new AssignToAll()'
// neue Objekte erstellt werden können -> privater Konstruktor und
} // Instanzieren nur mit Methode 'singleton(), wenn noch KEINE Instanz da'
public Scene getScene() {
return scene;
}
public void setScene(Scene scene) {
this.scene = scene;
}
public BaseController getBaseController() {
return baseController;
}
public void setBaseController(BaseController baseController) {
assert baseController == null || baseController.getFxmlParent() != null
: noFxmlParentMsg(BASE_FXML);
this.baseController = baseController;
}
public MenuBarAreaController getMenuBarAreaController() {
return menuBarAreaController;
}
public void setMenuBarAreaController(MenuBarAreaController menuBarAreaController) {
assert menuBarAreaController == null || menuBarAreaController.getFxmlParent() != null
: noFxmlParentMsg(MENUBAR_AREA_FXML);
this.menuBarAreaController = menuBarAreaController;
}
public ContentAreaController getContentAreaController() {
return contentAreaController;
}
public void setContentAreaController(ContentAreaController contentAreaController) {
assert contentAreaController == null || contentAreaController.getFxmlParent() != null
: noFxmlParentMsg(CONTENT_AREA_FXML);
this.contentAreaController = contentAreaController;
}
private static String noFxmlParentMsg(String fxmlPath) {
return "FXML-File '" + fxmlPath + "': fx:id des Wurzelelem. MUSS wegen "
+ "Controller-Basisklasse 'MyFxmlController' zwingend 'fxmlParent' sein";
}
/**
*
* @param <P> Typ der Wurzel-Pane der gegebenen FXML-Datei (BorderPane, HBox, VBox, ...)
* @param <TT> Typ des Controllers (BaseController, MenuBarAreaController, ...)
* @param fxmlPath relativer Pfad der FXML-Datei (auch unter Windows '/' als Verzeichnis-Trenner
* - Werte in AccessToAll als Konstante vorhanden (BASE_FXML, MENUBAR_AREA_FXML,
* CONTENT_AREA_FXML)
*
* @return C Generic Type für den zurückzuliefernden Controller
*/
public <P extends Pane, TT extends FxmlControllerIface<P>> TT createFromFxml(String fxmlPath) {
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlPath));
try { // loader.load MUSS ZUERST erfolgen, damit 'loader' alles weiß!
loader.load(); // Parent parent = ... Rückgabewert nicht genutzt
} catch (IOException ioe) {
throw new IllegalArgumentException("Lade-Problem bei FXML-Datei", ioe);
}
return loader.getController();
}
// Methode, die eleganter in anderer Klasse enthalten wäre - Kompromiss wg. Kürze:
public static String eventInfo(Event event) {
String txt = "Event Typ " + event.getEventType() + ", ausgelöst von ";
Object evtSrc = event.getSource();
if (evtSrc instanceof MenuItem mi) {
txt = "MenuItem '" + mi.getText() + "'";
} else if (evtSrc instanceof Button bt) {
txt = "Button '" + bt.getText() + "'";
} else if (evtSrc instanceof TextField tf) {
txt = "TextField '" + tf.getText() + "'";
} else if (evtSrc instanceof Pane pn) {
txt = "Pane - " + pn.toString();
} else if (evtSrc instanceof Control cl) {
txt = "Control - " + cl.toString();
} else if (evtSrc instanceof Node nd) {
txt = "Node - " + nd.toString();
} else {
txt = "Other Source Type - " + evtSrc.toString();
}
return txt;
}
}
5.4. Interface "my.demo.FxmlControllerIface"
package my.demo;
import javafx.fxml.FXML;
import javafx.scene.layout.Pane;
public interface FxmlControllerIface<P extends Pane> {
P getFxmlParent();
@FXML
void initialize(); // called by the FXMLLoader when initialization is complete
}
5.5. FXML-Datei "${project-dir}/src/my/demo/view/Base.fxml"
Siehe oben - diese wurde zur Erläuterung des FXML-Datei-Aufbaus genutzt
5.6. Klasse "my.demo.view_ctlr.BaseController"
/**
* Sample Skeleton for 'MyDemoAppBase.fxml' Controller Class
*/
package my.demo.view_ctlr;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import my.demo.FxmlControllerIface;
public class BaseController implements FxmlControllerIface<BorderPane> {
@FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
@FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
@FXML
private BorderPane fxmlParent;
@FXML // fx:id="btnDrueckenFuerMenue"
private Button btnDrueckenFuerMenue; // Value injected by FXMLLoader
@FXML
private Label lblInfoToDo;
@FXML
void menueZeigenAction(ActionEvent event) throws IOException {
System.out.println("Menü wird instanziert und aktiviert - b");
// MenuBarAreaController menuBarAreaCtlr = AccessToAll.singleton()
// .<HBox, MenuBarAreaController>createFromFxml(AccessToAll.MENUBAR_AREA_FXML);
// Explizite Rückgabe-Typ-Parameter korrekt, aber unnötig (Compiler kann's selbst):
MenuBarAreaController menuBarAreaCtlr = AccessToAll.singleton()
.createFromFxml(AccessToAll.MENUBAR_AREA_FXML);
Parent menuBarArea = menuBarAreaCtlr.getFxmlParent();
AccessToAll.singleton().setMenuBarAreaController(menuBarAreaCtlr);
fxmlParent.setTop(menuBarArea);
btnDrueckenFuerMenue.setDisable(true);
lblInfoToDo.setDisable(false);
}
@Override
@FXML // This method is called by the FXMLLoader when initialization is complete
public void initialize() {
assert fxmlParent != null : "fx:id=\"fxmlParent\" was not injected:"
+ " check your FXML file 'ContentArea.fxml'.";
assert btnDrueckenFuerMenue != null : "fx:id=\"btnDrueckenFuerMenue\" was not injected:"
+ " check your FXML file 'MyDemoAppBase.fxml'.";
assert lblInfoToDo != null : "fx:id=\"lblInfoToDo\" was not injected:"
+ " check your FXML file 'Base.fxml'.";
// hier könnten weitere dynamische Setup-Aktivitäten erfolgen ...
}
@Override
public BorderPane getFxmlParent() {
return fxmlParent;
}
}
5.7. FXML-Datei "${project-dir}/src/my/demo/view/MenuBarArea.fxml"
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.layout.HBox?>
<HBox xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1" fx:controller="my.demo.view_ctlr.MenuBarAreaController">
<children>
<MenuBar styleClass="menu-element">
<menus>
<Menu mnemonicParsing="false" styleClass="menu-element" text="File">
<items>
<MenuItem mnemonicParsing="false" onAction="#infoNotImplemented" styleClass="menu-element" text="Close" />
</items>
</Menu>
<Menu mnemonicParsing="false" styleClass="menu-element" text="Edit">
<items>
<MenuItem mnemonicParsing="false" onAction="#infoNotImplemented" styleClass="menu-element" text="Delete" />
</items>
</Menu>
<Menu mnemonicParsing="false" styleClass="menu-element" text="Settings">
<items>
<MenuItem mnemonicParsing="false" onAction="#setupContentArea" styleClass="menu-element" text="Inhaltsbereich aktivieren" />
<MenuItem mnemonicParsing="false" onAction="#tabAdd" text="Tab hinzufügen" />
</items>
</Menu>
<Menu mnemonicParsing="false" styleClass="menu-element" text="Help">
<items>
<MenuItem mnemonicParsing="false" onAction="#infoNotImplemented" styleClass="menu-element" text="About" />
</items>
</Menu>
</menus>
</MenuBar>
</children>
</HBox>
5.8. Klasse "my.demo.view_ctlr.MenuBarAreaController"
package my.demo.view_ctlr;
import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import my.demo.FxmlControllerIface;
public class MenuBarAreaController implements FxmlControllerIface<HBox> {
@FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
@FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
@FXML
private HBox fxmlParent;
@FXML
private MenuItem mniTabAdd;
@FXML
public void infoNotImplemented(ActionEvent event) {
System.out.println("Noch nicht implementiert - " + AccessToAll.eventInfo(event));
}
@FXML
public void setupContentArea(ActionEvent event) throws IOException {
System.out.println(AccessToAll.eventInfo(event));
ContentAreaController contentAreaCtlr = AccessToAll.singleton()
.<VBox, ContentAreaController>createFromFxml(AccessToAll.CONTENT_AREA_FXML);
AccessToAll.singleton().setContentAreaController(contentAreaCtlr);
AccessToAll.singleton().getBaseController().getFxmlParent()
.setCenter(contentAreaCtlr.getFxmlParent());
mniTabAdd.setDisable(false);
}
@FXML
public void showAbout(ActionEvent event) {
String msg = "Die beste JavaFX-App - definitiv!";
ButtonType btEnough = new ButtonType("Genug Info");
ButtonType btShowMore = new ButtonType("Zeige mehr Info");
ButtonType btShowAll = new ButtonType("Zeige komplette Info");
Alert alertConfirm = new Alert(AlertType.CONFIRMATION, msg, //
btEnough, btShowMore, btShowAll);
alertConfirm.setHeaderText("RX-HeaderText");
alertConfirm.setTitle("RX-Title");
Optional<ButtonType> result = alertConfirm.showAndWait();
if (result.get() == btEnough) {
System.out.println("genug Info ... Ende!");
} else if (result.get() == btShowMore) {
System.out.println("mehr Info ... OK");
Alert alertInfoMore = new Alert(AlertType.INFORMATION, "Hier mehr Info");
alertInfoMore.show();
} else {
System.out.println("alle Info ... wird gemacht");
Alert alertInfoAll = new Alert(AlertType.WARNING, "mehr gibt's nicht!");
alertInfoAll.show();
}
}
@FXML
public void tabAdd(ActionEvent event) {
System.out.println(AccessToAll.eventInfo(event));
List<Tab> tabs = AccessToAll.singleton().getContentAreaController().getTbpContentTabPane()
.getTabs();
int nr = tabs.size() + 1;
VBox contentVBox = new VBox();
Tab newTab = new Tab("Tab " + nr, contentVBox);
contentVBox.getChildren().add(new Label("Inhalt von Tab " + nr));
tabs.add(newTab);
}
@FXML
public void resetApp(ActionEvent event) throws IOException {
System.out.println(AccessToAll.eventInfo(event));
BaseController baseCtlr = AccessToAll.singleton()
.<BorderPane, BaseController>createFromFxml(AccessToAll.BASE_FXML);
Parent root = baseCtlr.getFxmlParent();
AccessToAll.singleton().getScene().setRoot(root); // Scene erhält neue Rootcontainer-Instanz
AccessToAll.singleton().setBaseController(baseCtlr); // neue BaseController-Instanz
AccessToAll.singleton().setMenuBarAreaController(null); // alte Instanz nun ungültig
AccessToAll.singleton().setContentAreaController(null); // detto
}
@Override
@FXML // This method is called by the FXMLLoader when initialization is complete
public void initialize() {
assert fxmlParent != null : "fx:id=\"fxmlParent\" was not injected:"
+ " check your FXML file 'MenuBarArea.fxml'.";
assert mniTabAdd != null : "fx:id=\"mniTabAdd\" was not injected:"
+ " check your FXML file 'MenuBarArea.fxml'.";
}
@Override
public HBox getFxmlParent() {
return fxmlParent;
}
}
5.9. FXML-Datei "${project-dir}/src/my/demo/view/ContentArea.fxml"
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/16" fx:controller="my.demo.view_ctlr.ContentAreaController">
<children>
<TabPane fx:id="tbpContentTabPane" prefHeight="200.0" prefWidth="349.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab closable="false" text="Tab 1">
<content>
<VBox prefHeight="169.0" prefWidth="271.0">
<children>
<Label text="Erster Tab, auf nicht schließbar gesetzt" />
</children>
</VBox>
</content>
</Tab>
</tabs>
</TabPane>
</children>
</VBox>
5.10. Klasse "my.demo.view_ctlr.ContentAreaController"
/**
* Sample Skeleton for 'ContentArea.fxml' Controller Class
*/
package my.demo.view_ctlr;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.scene.control.TabPane;
import javafx.scene.layout.VBox;
import my.demo.FxmlControllerIface;
public class ContentAreaController implements FxmlControllerIface<VBox> {
@FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
@FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
@FXML
private VBox fxmlParent;
@FXML // fx:id="tbpContentTabPane"
private TabPane tbpContentTabPane; // Value injected by FXMLLoader
@Override
@FXML // This method is called by the FXMLLoader when initialization is complete
public void initialize() {
//super.initialize();
assert fxmlParent != null : "fx:id=\"fxmlParent\" was not injected:"
+ " check your FXML file 'ContentArea.fxml'.";
assert tbpContentTabPane != null : "fx:id=\"tbpContentTabPane\" was not injected:"
+ " check your FXML file 'ContentArea.fxml'.";
}
public TabPane getTbpContentTabPane() {
return tbpContentTabPane;
}
@Override
public VBox getFxmlParent() {
return fxmlParent;
}
}
5.11. Stylesheet "${project-dir}/src/my/demo/my-demo-fxmlapp.css"
Das Styling erfolgt komplett im Stylesheet
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
.root { /*Styling-Basisklasse*/
-fx-font-size: 14pt;
/*-fx-font-family: "Courier New";*/
-fx-text-fill: blue;
-fx-background-color: rgb(255, 255, 223);
}
.menu-element {
-fx-border-width: 2px;
-fx-border-style: solid;
-fx-border-color: red;
-fx-border-radius: 0.9em;
}
#base-borderpane {
-fx-border-width: 2px;
-fx-border-style: solid;
-fx-border-color: darkviolet;
-fx-border-radius: 1.0em;
}
5.12. FxmlApp in Aktion






Der Konsolen-Output nach einem Zyklus, nach Reset bis zum Öffnen eines Tabs:
Menü wird instanziert und aktiviert - b MenuItem 'Inhaltsbereich aktivieren' MenuItem 'Tab hinzufügen' MenuItem 'Reset App' Menü wird instanziert und aktiviert - b MenuItem 'Inhaltsbereich aktivieren' MenuItem 'Tab hinzufügen'
6. Scribble
https://openjfx.io/javadoc/18/javafx.fxml/javafx/fxml/doc-files/introduction_to_fxml.html [Introduction to FXML | JavaFX 18] … 24.5.2022, 10:33:13
https://docs.oracle.com/javafx/2/best_practices/jfxpub-best_practices.htm [Implementing JavaFX Best Practices | JavaFX 2 Tutorials and Documentation] … 2021-05-18 Sehr gut, zeigt u.a. MVC mit FXML!
https://sodocumentation.net/javafx [javafx - Getting started with javafx | javafx Tutorial] … 2021-04-24
https://sodocumentation.net/javafx/topic/1580/fxml-and-controllers [javafx - FXML and Controllers | javafx Tutorial] … 2021-04-24
Links:
Introduction to FXML | JavaFX 18 … 2022-05-18
JavaFX TreeView item dynamic loading demo with FXML. … 2021-04-16
can you use fxml include for dynamically adding/removing gui objects? (JavaFX forum at Coderanch) … 2021-04-16
fxml - JavaFX controller communication with dynamic content - Stack Overflow … 2021-04-16
Introduction to FXML | JavaFX 16 … 2021-04-18
javafx - FXML and Controllers | javafx Tutorial … 2021-04-27
https://stackoverflow.com/questions/40246788/fxml-run-after-initialized [java - FXML: "Run after initialized" - Stack Overflow] … 2021-05-18 Beschreibt Procedere des FXMLLoaders etc.
(Solved) What is the justification for JavaFX FXML schema not supporting context menu inside the panes? - Local Coder … 24.5.2022, 09:23:42
javafx 2 - Using FXML to Create ContextMenu within a Pane - Stack Overflow … 24.5.2022, 09:31:13
https://edencoding.com/dependency-injection/ [Complete Guide To FXML Dependency Injection and Controller Initialisation – Eden Coding] … 2023-06-07
Weiteres …