jaxoncreed
jaxoncreed

Reputation: 1128

Calling methods in a Java Fx ActionEvent

I'm new to JavaFX and I want to create a view class that will call methods in a Controller when an event is triggered by a button push. My code is below:

package spacetrader.menu;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import spacetrader.Window;

public class MenuView implements Initializable {
    public Window window;
    public MenuCtrl menuCtrl;

    @FXML
    Button start;
    @FXML
    Pane background;
    @FXML
    Button exit;

    public MenuView() {};

    public MenuView(Window aWindow, MenuCtrl aMenuCtrl) {
        window = aWindow;
        menuCtrl = aMenuCtrl;
    }

    void renderMainMenu() {
        try {
            window.loadFXML(new FXMLLoader((getClass().getResource("MainMenu.fxml"))));
        } catch (IOException ex) {
            Logger.getLogger(MenuView.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        start.setOnAction((ActionEvent event) -> menuCtrl.newGame());
        exit.setOnAction((ActionEvent event) -> menuCtrl.closeApplication());
    }
}

This compiles, but when I run it and press the button I get a null pointer exception on the lambda expressions. The exception refers to "menuCtrl." How should I be setting up my program to make the button call menuCtrl.newGame()?

Here's what I've tried already:

Upvotes: 0

Views: 4268

Answers (3)

James_D
James_D

Reputation: 209235

Your setup is unusual, in that your controller class (which you've called MenuView) is the class that calls (indirectly, via your Window class) the FXMLLoader's load method. The FXMLLoader parses the FXML, sees the fx:controller attribute, and consequently instantiates it using the default (no-argument) constructor. (I am making an assumption here that you don't call setController or setControllerFactory on the FXMLLoader in Window.loadFXML.) Thus you have two controller instances: the one which created the loader (for whom the initialize() method is never called and the @FXML-annotated fields are never injected), and one which was created by the FXMLLoader (which called the no-argument constructor, so the window and menuCtrl fields are not initialized for that instance).

You should either refactor this, so that the FXMLLoader is created and passed to the Window class for loading elsewhere (i.e. without instantiating the controller), or you should explicitly set the controller on the FXMLLoader. You can achieve the latter in two steps:

  1. Remove the fx:controller attribute from MainMenu.fxml
  2. Set the controller explicitly to the instance already created in your renderMainMenu method:

--

FXMLLoader loader = new FXMLLoader((getClass().getResource("MainMenu.fxml"))) ;
loader.setController(this);
window.loadFXML(loader);

Upvotes: 1

Ashik Ali
Ashik Ali

Reputation: 11

You may try using Scene Builder where drag and drop feature is available and you can design a screen in seconds. In code section you just have to declare method name and implement that method in code handler class.

You can get it from here

Upvotes: 0

Puce
Puce

Reputation: 38122

You have two constructors. My guess is that you're loading an FXML somewhere which will instantiate your controller using the no-args constructor. In this case menuCtrl is not initialized.

If you're instantiating menuCtrl in the FXML, then add a @FXML annotation to the field.

Upvotes: 0

Related Questions