Issue
I'm using MenuButton in fxml to switch between tabs. The problem is, the menubuttons throw exceptions, but if I change it to a simple button, it doesn't. Here's my fxml code:
<MenuButton mnemonicParsing="false" prefHeight="27.0" prefWidth="101.0" style="-fx-background-color: #666666;" text="Select menu" textFill="WHITE" GridPane.columnIndex="4">
<items>
<MenuItem mnemonicParsing="false" onAction="#switchToMoney" text="1" />
<MenuItem mnemonicParsing="false" onAction="#switchToWeight" text="2" />
<MenuItem mnemonicParsing="false" onAction="#switchToTemperature" text="3" />
</items>
</MenuButton>
In my controller class:
@FXML
private void switchToMoney() throws IOException {
App.setRoot("money");
}
@FXML
private void switchToWeight() throws IOException {
App.setRoot("weight");
}
@FXML
private void switchToTemperature() throws IOException {
App.setRoot("temperature");
}
and the exception I get:
"C:\Program Files\Java\jdk-14\bin\java.exe" --add-modules javafx.base,javafx.graphics --add-reads javafx.base=ALL-UNNAMED --add-reads javafx.graphics=ALL-UNNAMED "-javaagent:D:\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar=64566:D:\IntelliJ IDEA 2019.3.4\bin" -Dfile.encoding=UTF-8 -p "C:\Users\tarbe\.m2\repository\org\openjfx\javafx-base\14\javafx-base-14-win.jar;C:\Users\tarbe\.m2\repository\org\openjfx\javafx-graphics\14\javafx-graphics-14-win.jar;D:\IntelliJ IDEA 2019.3.4\projects\proba\target\classes;C:\Users\tarbe\.m2\repository\org\openjfx\javafx-controls\14\javafx-controls-14.jar;C:\Users\tarbe\.m2\repository\org\openjfx\javafx-controls\14\javafx-controls-14-win.jar;C:\Users\tarbe\.m2\repository\org\openjfx\javafx-graphics\14\javafx-graphics-14.jar;C:\Users\tarbe\.m2\repository\org\openjfx\javafx-base\14\javafx-base-14.jar;C:\Users\tarbe\.m2\repository\org\openjfx\javafx-fxml\14\javafx-fxml-14.jar;C:\Users\tarbe\.m2\repository\org\openjfx\javafx-fxml\14\javafx-fxml-14-win.jar" -m org.alkfejl/org.alkfejl.App
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:222)
at javafx.controls/javafx.scene.control.skin.MenuButtonSkinBase.lambda$new$7(MenuButtonSkinBase.java:198)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:832)
Process finished with exit code 0
EDIT: Here's my code of the App class, which extends the Application. the App class:
public class App extends Application {
private static Scene scene;
@Override
public void start(Stage stage) throws IOException {
scene = new Scene(loadFXML("general"));
stage.setTitle("Calculator");
stage.setScene(scene);
stage.show();
}
static void setRoot(String fxml) throws IOException {
scene.setRoot(loadFXML(fxml));
}
private static Parent loadFXML(String fxml) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
return fxmlLoader.load();
}
public static void main(String[] args) {
launch();
}
}
Solution
Your error can be reproduced with this minimal example1:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
MenuButton btn = new MenuButton("Switch...");
MenuItem item = new MenuItem("To 'Hello, World!'");
item.setOnAction(
e -> {
StackPane root = new StackPane(new Label("Hello, World!"));
primaryStage.getScene().setRoot(root);
});
btn.getItems().add(item);
primaryStage.setScene(new Scene(new StackPane(btn), 500, 300));
primaryStage.show();
}
}
Which outputs:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Objects.java:222)
at javafx.controls/javafx.scene.control.skin.MenuButtonSkinBase.lambda$new$7(MenuButtonSkinBase.java:198)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:832)
There are at least two simple ways to "fix" the NPE:
Hide the
MenuButton
before changing the root of theScene
.StackPane root = new StackPane(new Label("Hello, World!")); btn.hide(); primaryStage.getScene().setRoot(root);
If you're using FXML then you'll need to inject the
MenuButton
into the FXML controller instance. This is accomplished by giving the element anfx:id="foo"
attribute in the FXML file and adding an@FXML private MenuButton foo;
field to the controller. Then you would invokefoo.hide()
before changing the root of theScene
.See Introduction to FXML for more information about FXML.
Change the
Scene
of theStage
instead of changing the root of theScene
.StackPane root = new StackPane(new Label("Hello, World!")); primaryStage.setScene(new Scene(root, 500, 300));
Both the example and solutions were tested using JavaFX 14.0.1.
1. Note this sort of example is what should be provided by you, in the question.
Answered By - Slaw