I'm currently working on my first FXML project and also trying to implement the MVC format. My project is to build a calculator and my way of proceeding is by having a base fxml file with a menu that when the onAction of the menuItems are called, they load a FXML file onto the anchorPane that is in my base FXML file. The base.fxml file has now controller, I only included a MenuBar with its respective controller. What I ultimately want is a way to load a the fxml file and use it's functions in another fxml file (base.fxml). When I click on any menuItem, it gives a very long error ending in a NullPointerException at the line of the FXMLLoader.
This is the onAction called when a menuItem is pressed (the only thing that changes is the file called depending on the type of calculator the user wants) :
public void modeStandard() throws IOException {
Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("standard.fxml")));
anchorPane.getChildren().clear();
anchorPane.getChildren().add(root);
}
The anchorPane has an id that is used in base.fxml (shown at the end). All my onActions are called at their respective locations and the controllers are also assigned correctly
For reference, this is my base.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" >
<children>
<fx:include source="MenuBar.fxml" fx:id="menuBar"/>
<SplitPane dividerPositions="0.6546822742474916" prefHeight="429.0" prefWidth="600.0">
<items>
<AnchorPane fx:id="anchorPane" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<TableView prefHeight="371.0" prefWidth="200.0">
<columns>
<TableColumn prefWidth="198.0" text="Résultats précédents" />
</columns>
</TableView>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</VBox>
And this is menuBar.fxml, which is included :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<MenuBar prefHeight="0.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo.modele.MenuBarController">
<menus>
<Menu mnemonicParsing="false" text="Standard">
<items>
<MenuItem onAction="#modeStandard" mnemonicParsing="false" text="Calculatrice standard" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Scientifique">
<items>
<MenuItem onAction="#modeScientifique" mnemonicParsing="false" text="Calculatrice scientifique" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Graphique">
<items>
<MenuItem onAction="#modeGraphique" mnemonicParsing="false" text="Calculatrice graphique" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Programmeur">
<items>
<MenuItem onAction="#modeProgrammeur" mnemonicParsing="false" text="Calculatrice programmeur" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Conversion">
<items>
<MenuItem onAction="#appelerConvertisseur" mnemonicParsing="false" text="Appeller convertisseur" />
</items>
</Menu>
</menus>
</MenuBar>
The MenuBarController is basically the first function I've shown ( modeStandard() ) multiplied 4 times for the different modes and the anchorPane and menuBar so I can id them :
@FXML
private AnchorPane anchorPane;
@FXML
private MenuBar menuBar;
And finally this is an example of what im trying to load onto the anchorPane in base.fxml :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:controller="com.example.demo.modele.Standard" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
<children>
<VBox alignment="CENTER" layoutX="6.0" prefHeight="371.0" prefWidth="405.0">
<children>
<Label alignment="CENTER" text="0" translateX="80.0" />
<GridPane alignment="CENTER">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="66.0" minWidth="10.0" prefWidth="68.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="66.0" minWidth="10.0" prefWidth="68.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="66.0" minWidth="66.0" prefWidth="66.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button alignment="BASELINE_CENTER" mnemonicParsing="false" prefHeight="26.0" prefWidth="66.0" text="+" />
<Button mnemonicParsing="false" prefHeight="26.0" prefWidth="66.0" text="-" GridPane.columnIndex="1" />
<Button mnemonicParsing="false" prefHeight="27.0" prefWidth="66.0" text="x" GridPane.rowIndex="1" />
<Button mnemonicParsing="false" prefHeight="27.0" prefWidth="66.0" text="÷" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Button mnemonicParsing="false" prefHeight="26.0" prefWidth="66.0" text="^2" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" prefHeight="26.0" prefWidth="66.0" text="√" GridPane.columnIndex="1" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" prefHeight="26.0" prefWidth="66.0" text="^-1" GridPane.columnIndex="2" />
<Button mnemonicParsing="false" prefHeight="16.0" prefWidth="66.0" text="-x" GridPane.columnIndex="2" GridPane.rowIndex="1" />
<Button mnemonicParsing="false" prefHeight="27.0" prefWidth="66.0" text="AC" GridPane.columnIndex="2" GridPane.rowIndex="2" />
<Button mnemonicParsing="false" prefWidth="66.0" text="7" GridPane.rowIndex="3" />
<Button mnemonicParsing="false" prefWidth="66.0" text="8" GridPane.columnIndex="1" GridPane.rowIndex="3" />
<Button mnemonicParsing="false" prefWidth="66.0" text="9" GridPane.columnIndex="2" GridPane.rowIndex="3" />
<Button mnemonicParsing="false" prefWidth="66.0" text="4" GridPane.rowIndex="4" />
<Button mnemonicParsing="false" prefWidth="66.0" text="5" GridPane.columnIndex="1" GridPane.rowIndex="4" />
<Button mnemonicParsing="false" prefWidth="66.0" text="6" GridPane.columnIndex="2" GridPane.rowIndex="4" />
<Button mnemonicParsing="false" prefWidth="66.0" text="DEL" GridPane.rowIndex="6" />
<Button mnemonicParsing="false" prefWidth="66.0" text="2" GridPane.columnIndex="1" GridPane.rowIndex="5" />
<Button mnemonicParsing="false" prefWidth="66.0" text="1" GridPane.rowIndex="5" />
<Button mnemonicParsing="false" prefWidth="66.0" text="0" GridPane.columnIndex="1" GridPane.rowIndex="6" />
<Button mnemonicParsing="false" prefWidth="66.0" text="3" GridPane.columnIndex="2" GridPane.rowIndex="5" />
<Button mnemonicParsing="false" prefWidth="66.0" text="." GridPane.columnIndex="2" GridPane.rowIndex="6" />
</children>
</GridPane>
<Button onAction="#notationSansPriorite" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" prefWidth="200.0" text="=" translateY="-30.0"/>
</children>
</VBox>
</children>
</AnchorPane>
Here is the error :
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml@19/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1857)
at javafx.fxml@19/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1724)
at javafx.base@19/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.base@19/javafx.event.Event.fireEvent(Event.java:198)
at javafx.controls@19/javafx.scene.control.MenuItem.fire(MenuItem.java:459)
at javafx.controls@19/com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1385)
at javafx.controls@19/com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.lambda$createChildren$12(ContextMenuContent.java:1338)
at javafx.base@19/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)
at javafx.base@19/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
at javafx.base@19/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base@19/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base@19/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base@19/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base@19/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics@19/javafx.scene.Scene$MouseHandler.process(Scene.java:3894)
at javafx.graphics@19/javafx.scene.Scene.processMouseEvent(Scene.java:1887)
at javafx.graphics@19/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2620)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:411)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:301)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:450)
at javafx.graphics@19/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)
at javafx.graphics@19/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:449)
at javafx.graphics@19/com.sun.glass.ui.View.handleMouseEvent(View.java:551)
at javafx.graphics@19/com.sun.glass.ui.View.notifyMouse(View.java:937)
at javafx.graphics@19/com.sun.glass.ui.mac.MacView.notifyMouse(MacView.java:127)
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:116)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:77)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at javafx.base@19/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
at javafx.fxml@19/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:84)
at javafx.fxml@19/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1854)
... 40 more
Caused by: java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:233)
at com.example.demo/com.example.demo.modele.MenuBarController.modeStandard(MenuBarController.java:50)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
... 47 more
this is my best attempt at a minimal reproducible example. There are 2 controllers and 2 fxml files, respectively named BaseController with base.fxml and ButtonController with button.fxml .
Firstly is HelloApplication :
package com.example.test;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("base.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Shown here is BaseController :
package com.example.test;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.layout.VBox;
import java.io.IOException;
public class BaseController {
@FXML
private VBox vBoxBase;
public void importButton() throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("button.fxml"));
vBoxBase = (VBox) root;
}
}
And here is base.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.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane fx:controller="com.example.test.BaseController" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
<children>
<MenuBar layoutY="8.0">
<menus>
<Menu mnemonicParsing="false" text="Import">
<items>
<MenuItem onAction="#importButton" mnemonicParsing="false" text="Import button" />
</items>
</Menu>
</menus>
</MenuBar>
<VBox fx:id="vBoxBase" layoutX="250.0" layoutY="100.0" prefHeight="200.0" prefWidth="100.0" />
</children>
</AnchorPane>
Now here is ButtonController :
package com.example.test;
public class ButtonController {
public void buttonFonction() {
System.out.println("button pressed");
}
}
And button.fxml finally :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox fx:controller="com.example.test.ButtonController" alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button onAction="#buttonFonction" mnemonicParsing="false" text="Imported Button" />
<Label text="Imported label" />
</children>
</VBox>
My goal is that button.fxml is imported onto vBoxBase when the MenuItem labeled "Import button" is pressed. I don't have any errors but when debugging, all values shown are null but the VBox has it's correct childset but these are also null. But, the onAction of the button is preserved.
P.S. this is my first post, if something is missing or you would like to see my project in it's entirety, I will gladly do anything in my power to help resolve this problem with you
Don't set FXML injected nodes to new values
In your
BaseControlleryou have:That will set the
vBoxBasereference to the node with the correspondingfx:idwhich is created by the FXMLLoader. The node is part of the node graph returned by the loader.Then later on you do this:
That will set the
vBoxBasenode to a different node from that which the FXMLLoader injected and which is not part of the node hierarchy returned by the original FXMLLoader invocation that created thevBoxBase, so the associated node you set is never attached to the scene graph and you can never see or interact with it.What you probably want to do instead
Modify the child scene graph associated with your container node
vBoxBase.You are performing this on a menu item selection, so every time you select the menu item it will add a new value to the
VBox.However, maybe you only want a single item in the VBox on menu selection, in which case you could do:
If you didn't want a new node graph for the root created each time you select a menu item, you could load it in the
initializemethod and store the reference in an instance variable, and reference that when you use it.If you want to use that pattern then FXML has a method for streamlining it, which I won't elaborate here, see nested controllers if interested.
If you only have a single item that you want to set, then (if you used a border pane for example) you could set it in a position in the border pane, possibly enclosing it in a stack pane for alignment, though I won't demonstrate that here.
General layout advice
Example
The example applies the fix for adding a child of the
vBoxBasementioned earlier, as well as some of the general layout advice.On execution, you get this output after the "Import button" menu item has been selected three times, to load the button and label FXML three times, and add it to the VBox used for the content of the application center panel.
BaseController.java
base.fxml
button.fxml
The rest of your code is unchanged (though I would advise fixing spelling errors in code, those kinds of things are always annoying and can lead to bugs).