1

I have a StackPane as my root, and have loaded a BorderPane layout over it in the Main Application Start() method. In the BorderPane layout I have 2 buttons: One to add an fxml file (Menu.fxml) and the other to remove this file. Once the Menu.fxml is loaded, I have a button in Menu.fxml which when clicked should load an fxml file (One.fxml) in the centre of the previously loaded BorderPane layout. However, at this stage I get a null pointer exception. Can you please point out where am I going wrong. Thank you.

Below is my code:


public class Main extends Application {

    private static StackPane rootStack;
    private static BorderPane rootBorder;

    @Override
    public void start(Stage stage) throws Exception {
        rootStack = FXMLLoader.load(getClass().getResource("Base.fxml"));
        rootBorder = FXMLLoader.load(getClass().getResource("Border.fxml"));
        rootStack.getChildren().addAll(rootBorder);

        Scene scene = new Scene(rootStack);
        stage.setScene(scene);
        stage.show();
    } // start

    public static StackPane getRootStack() {
        return rootStack;
    } // end of method getRootStack

    public static BorderPane getRootBorder() {
        return rootBorder;
    } // end of method getRootBorder

    public static void main(String[] args) {
        launch(args);
    } // launch

} // Main

public class BorderController {

    private StackPane rootStack = Main.getRootStack();
    VBox menu;

    public void initialize() throws IOException {
        menu = FXMLLoader.load(getClass().getResource("Menu.fxml"));
    } // initialize    

    @FXML
    private void addMenuPane(ActionEvent event) throws IOException {
        rootStack.getChildren().add(menu);
    } // addMenuPane

    @FXML
    private void removeNode(ActionEvent event) {
        rootStack.getChildren().remove(menu);
    } // removeNode

} // BorderController

public class MenuController {

    private BorderPane rootBorder = Main.getRootBorder();

    @FXML
    private JFXButton oneButton;

    public void initialize() {
        oneButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (event) -> {
            try {
                rootBorder.setCenter(FXMLLoader.load(getClass().getResource("One.fxml")));
            } catch (IOException ex) {
                Logger.getLogger(MenuController.class.getName()).log(Level.SEVERE, null, ex);
            }
        });

    } // initialize    

} // MenuController

Stack Trace:

Executing C:\Users\User\Documents\NetBeansProjects\Stack\dist\run2093523570\Stack.jar using platform C:\Program Files\Java\jdk1.8.0_121\jre/bin/java
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
    at stack.MenuController.lambda$initialize$0(MenuController.java:22)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.event.Event.fireEvent(Event.java:198)
    at javafx.scene.Scene$ClickGenerator.postProcess(Scene.java:3470)
    at javafx.scene.Scene$ClickGenerator.access$8100(Scene.java:3398)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3766)
    at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:381)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:417)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
    at com.sun.glass.ui.View.notifyMouse(View.java:937)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Thread.java:745)
Deleting directory C:\Users\User\Documents\NetBeansProjects\Stack\dist\run2093523570
5
  • Post the complete stack trace in the question. Commented Aug 28, 2017 at 18:58
  • It looks like rootBorder is null. Can you check that? Commented Aug 28, 2017 at 20:21
  • I can read your code. I am asking you to verify that rootBorder is null in the event handler in MenuController. Just put a debug line into the event handler, e.g. if (rootBorder == null) System.out.println("Warning: rootBorder is null"); and see if the message is displayed. Or put a breakpoint at the beginning of the event handler and run it in your debugger. Commented Aug 28, 2017 at 20:37
  • Yes...rootBorder is null at the event handler Commented Aug 28, 2017 at 20:55
  • OK, thanks: the reason for that is explained in my answer. Commented Aug 28, 2017 at 21:12

1 Answer 1

3

When you load an FXML file, if the root element of the FXML file has a fx:controller attribute, then the class specified by that attribute is instantiated, any @FXML-annotated fields are injected, and the initialize() method is invoked on the controller class instance (if there is one). All of this happens as part of the load() method of FXMLLoader.

If you consider the line of code in your Application.start() method:

rootBorder = FXMLLoader.load(getClass().getResource("Border.fxml"));

this will load Border.fxml, create the controller, call its initialize() method, etc, and only when all that is complete will it assign the result to rootBorder. The initialize() method in BorderController that is invoked loads Menu.fxml; that in turn instantiates the controller in Menu.fxml which (I assume) is MenuController. The initializer code for MenuController includes

private BorderPane rootBorder = Main.getRootBorder();

Note that at this point, the load() method that is loading Border.fxml still isn't finished, so the static rootBorder field in Main has not yet been assigned, and so Main.getRootBorder() returns null.

Consequently, later when you try to do

rootBorder.setCenter(...);

since rootBorder was assigned the value null, you get a null pointer exception.

A quick and dirty fix would be just to retrieve the border pane at the moment you need it:

public class MenuController {

    @FXML
    private JFXButton oneButton;

    public void initialize() {
        oneButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (event) -> {
            try {
                Main.getRootBorder().setCenter(FXMLLoader.load(getClass().getResource("One.fxml")));
            } catch (IOException ex) {
                Logger.getLogger(MenuController.class.getName()).log(Level.SEVERE, null, ex);
            }
        });

    } // initialize    

} // MenuController

This approach of using static fields in the Application subclass, however, is a really bad design. You introduce all kinds of unnecessary coupling (is your MenuController really supposed to rely on the existence of that particular Main class???) and expose all kinds of UI elements that shouldn't be exposed (suppose you want to change the overall layout at some point; you would basically need to change your entire UI code base). You should probably restructure this to avoid needing to do things this way.

Sign up to request clarification or add additional context in comments.

5 Comments

Thank you James_D for taking the time to explain what is causing this issue. Really appreciate it. The only reason I was using a static field in the Main class is because I wanted access to the BorderPane layout in other controller classes to load as per the menu selected by the user. Other than searching through tutorials on the internet (which led to the above result) I have no other resourse that can guide me in the right direction. I really enjoy working on JavaFX and want to learn the right way to restructure this particular project.
Is there a good design example for this scenario which you could point me to so that I could learn from it? I have been using the Dietel Java How to Program text book to learn JavaFX but it has limited material and only covers some basic concepts. Are there any good reference materials that I can use to learn JavaFX in more details? I genuinely want to learn and enhance my knowledge. Thank you for all your help.
@snow Have a look at github.com/acaicedo/Screen-Framework. If I have time later I'll elaborate on the answer.
Thank you for the github link. I also found the video by Angela Caicedo that explains this structure in great detail. Posting a link for other readers in case they reach here. youtube.com/watch?v=5GsdaZWDcdY
I have been reviewing the steps in my code to understand the loading process as you have explained, and noticed that the exact same code for rootStack works but not for borderStack. In the BorderController the rootStack is Not Null. Can you please share your comments as to why the same code is behaving differently for these two variables. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.