0

I have a Stage which loads MainView.fxml. This MainView.fxml includes three other views. In one of these other views I have buttons which I can fully modify without problems.

Here is my MainView.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.*?>

<BorderPane minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="1000.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.MainController">
   <left>
      <Pane prefHeight="800.0" prefWidth="300.0" BorderPane.alignment="CENTER">
         <children>
            <fx:include fx:id="ControlsView" source="ControlsView.fxml" />
            <fx:include fx:id="ParametersView" source="ParametersView.fxml" />
         </children>
      </Pane>
   </left>
    <center>
        <fx:include fx:id="CashRegisterView" source="CashRegisterView.fxml" />
    </center>
</BorderPane>

For example in the controller for ControlsController I have a method which modiefies buttons which just works fine:

@FXML
private void btStartActionEventHandler(MouseEvent event){
    if(controlsModel.getBtStartValue() == ControlsModel.buttonStates.Start){
        StartSuperSim();
        controlsModel.setBtStartValue(ControlsModel.buttonStates.Pause);
        btStart.setText(controlsModel.getBtStartValue().toString());
        btStop.setDisable(false);
    }
}

As you can clearly see I modify the properties of btStart and btStop.

But when I try to modify anything coming from the ParametersView.fxml it throws a java.lang.reflect.InvocationTargetException, java.lang.reflect.InvocationTargetException and a java.lang.NullPointerException. As shown here in the full stackTrace.

And here is the piece of code in question:

public class CashRegisterController extends BaseController{

    @FXML
    private Pane pCashRegister;

    @FXML
    public void CreateCashRegisters(){

        Pane pane = new Pane();
        pane.setStyle("-fx-background-color: black;");

        this.pCashRegister.getChildren().add(0, pane);
    }
}

I don't create a instance of pCashRegister because I don't do that in my ControlsController either. But when I do, it works fine but it doesn't add the pane to my view.

@FXML
private Button btStart;
@FXML
private Button btStop;

And at last my CashRegisterView.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.layout.Pane?>

<Pane fx:id="pCashRegister" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="700.0" xmlns="http://javafx.com/javafx/8.0.102" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.CashRegisterController">

Could somebody please explain how this works and what is happening here? I just can't seem to figure it out. I just think that for whatever reason the CashRegisterView.fxml doesn't link to my CashRegisterController even though I specified fx:controller="controller.CashRegisterController".

Edit:

The StartSuperSim() method:

private void StartSuperSim(){
    getCashRegisterController().CreateCashRegisters();

    seconds = 0;

    setSuperSim(new Timeline(new KeyFrame(Duration.seconds((1 / controlsModel.getAcceleration())), new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event) {
            seconds++;
            //System.out.println(seconds);

            if((seconds % (int)controlsModel.getParametersModel().getTimeBetweenCustomers()) == 0){
                //System.out.println("Fired");
            }
        }
    })));
    getSuperSim().setCycleCount(Timeline.INDEFINITE);
    getSuperSim().play();
}

And my MainController.java:

package controller;
public class MainController extends BaseController{

private ControlsController controlsController;
private ParametersController parametersController;
private CashRegisterController cashRegisterController;

public MainController() {
    controlsController = Controller.getInstance().getControlsController();
    parametersController = Controller.getInstance().getParametersController();
    cashRegisterController = Controller.getInstance().getCashRegisterController();
}
}

And the Controller.java class which I use to pass the instances around:

package controller;

public class Controller {
private final static Controller instance = new Controller();

public static Controller getInstance() {
    return instance;
}

private ControlsController controlsController = new ControlsController();
private ParametersController parametersController = new ParametersController();
private CashRegisterController cashRegisterController = new CashRegisterController();

public ControlsController getControlsController() {
    return controlsController;
}

public ParametersController getParametersController() {
    return parametersController;
}

public CashRegisterController getCashRegisterController() {
    return cashRegisterController;
}
}

And at last the full stacktrace:

    "D:\Program Files\Java\jdk1.8.0_102\bin\java" -Didea.launcher.port=7534 "-Didea.launcher.bin.path=D:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.2.5\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_102\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_102\jre\lib\rt.jar;D:\This PC\Documents\Projects\Java\SuperSim\out\production\SuperSim;D:\Program Files (x86)\JetBrains\IntelliJ IDEA 2016.2.5\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMain Main
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    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:380)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:294)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:416)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:415)
    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)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1769)
    ... 31 more
Caused by: java.lang.NullPointerException
    at controller.CashRegisterController.CreateCashRegisters(CashRegisterController.java:26)
    at controller.ControlsController.StartSuperSim(ControlsController.java:95)
    at controller.ControlsController.btStartActionEventHandler(ControlsController.java:69)
    ... 41 more
  • Show the `StartSuperSim()` (sic) method and also show how you are getting the reference to the `CashRegisterController` in the `MainController`. Also, please post the stack trace in the question, instead of a link to it. – James_D Jan 16 '17 at 12:50
  • I've updated the question @James_D! –  Jan 16 '17 at 13:01

1 Answers1

1

The instance of CashRegisterController that you create and store in your Controller singleton is not the same instance that is created by the FXMLLoader when it loads CashRegisterView.fxml. Of course, the FXMLLoader initializes the @FXML-annotated fields on the instance it creates (it doesn't have access to the instance stored in the singleton, unless you have a very complex controller factory you haven't shown).

You should use the Nested Controllers technique shown in the documentation to access the controllers for the included FXML files. In short, in your MainController you can do:

package controller;
public class MainController extends BaseController{

    // field names are formed by appending "Controller" to the fx:id attribute value:
    @FXML
    private ControlsController ControlsViewController;
    @FXML
    private ParametersController ParametersViewController;
    @FXML
    private CashRegisterController CashRegisterViewController;

    // Remove this constructor:
    // public MainController() {
        //controlsController = Controller.getInstance().getControlsController();
        //parametersController = Controller.getInstance().getParametersController();
        //cashRegisterController = Controller.getInstance().getCashRegisterController();
    // }

    public void initialize() {
        // give ControlsViewController access to CashRegisterViewController:
        ControlsViewController.setCashRegisterViewController(CashRegisterViewController);
    }
}

and then just define the obvious injection method in ControlsController:

public class ControlsController {

    private CashRegisterController cashRegisterController ;

    public void setCashRegisterViewController(CashRegisterController cashRegisterController) {
        this.cashRegisterController = cashRegisterController ;
    }

    // existing code...

    private void StartSuperSim(){

        // getCashRegisterController().CreateCashRegisters();

        cashRegisterController.CreateCashRegisters();

        seconds = 0;

        setSuperSim(new Timeline(new KeyFrame(Duration.seconds((1 / controlsModel.getAcceleration())), new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                seconds++;
                //System.out.println(seconds);

                if((seconds % (int)controlsModel.getParametersModel().getTimeBetweenCustomers()) == 0){
                    //System.out.println("Fired");
                }
            }
        })));
        getSuperSim().setCycleCount(Timeline.INDEFINITE);
        getSuperSim().play();
    }

    // ...
}

The Controller singleton class you defined is not going to do anything useful, because the controller instances it is storing are not the ones connected to the corresponding FXML files. Consequently those controller instances can't do anything that affects the UI that is displayed (for example, none of their @FXML-annotated fields will be initialized). You should remove that class and all references to it.

A couple of other notes: first, it's not really very good practice for controllers to have access to one another in the way you tried to do here and I showed. It's probably better to use a MVC/MVP type design (you have views and controllers, but there is no model anywhere). Then all the controllers just share a model instance, and access and observe the same data within it. Have a look at Applying MVC With JavaFx for a simple example.

Finally, please use proper naming conventions. It makes it easier for others to read and understand your code, and syntax-highlighting software (such as that used by this site) will better parse your code and highlight it correctly.

Community
  • 1
  • 1
James_D
  • 201,275
  • 16
  • 291
  • 322
  • You helped me immensly once again, thank you so much for you effort James! And as you probably can see, I'm still figuring out how to do all of this properly, thanks so much again for you time. –  Jan 16 '17 at 14:14
  • Hey James. I've tried to use your example that you showed under the "MVC With JavaFx" link, but whenever I use the model instance within for example `ControlsController` in another method, it throws a `NullPointerException`. Any ideas? –  Jan 16 '17 at 16:12
  • Without seeing the code, no. (Other than the obvious: you are dereferencing a reference that is null.) This would be a different question, but before you post it, figure out which variable is actually null and why you think it should not be null. – James_D Jan 16 '17 at 16:13