Reputation: 1128
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
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:
fx:controller
attribute from MainMenu.fxmlrenderMainMenu
method:--
FXMLLoader loader = new FXMLLoader((getClass().getResource("MainMenu.fxml"))) ;
loader.setController(this);
window.loadFXML(loader);
Upvotes: 1
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
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