I created a custom component called PlayerView
which has an associated PlayerView.fxml FXML layout and PlayerView.java class to instantiate the component. I then include multiple instances of this component in another FXML Layout called Board.fxml and attempt to refer to these instances in Board's controller Board.java by using the @FXML
annotation to create an injection. However, this injection does not work as intended since I get a NullPointerException
when I attempt to refer to PlayerView
instances in my controller.
PlayerView.fxml
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml">
...
</fx:root>
PlayerView.java
public class PlayerView extends VBox {
public PlayerView() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("PlayerView.fxml"));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
public void init(String name) {
...
}
}
Board.fxml
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="pablo.Board" prefHeight="400.0" prefWidth="600.0" alignment="CENTER">
<BorderPane>
<top>
<PlayerView BorderPane.alignment="TOP_CENTER" fx:id="playerView2"/>
</top>
<bottom>
<PlayerView BorderPane.alignment="BOTTOM_CENTER" fx:id="playerView1"/>
</bottom>
<left>
<PlayerView BorderPane.alignment="CENTER_LEFT" fx:id="playerView3"/>
</left>
<right>
<PlayerView BorderPane.alignment="CENTER_RIGHT" fx:id="playerView4"/>
</right>
...
</BorderPane>
...
</VBox>
Board.java
public class Board extends Application {
...
@FXML PlayerView playerView1, playerView2, playerView3, playerView4;
@Override
public void start(Stage primaryStage) throws IOException, ClassNotFoundException {
Parent root = FXMLLoader.load(getClass().getResource("Board.fxml"));
Scene scene = new Scene(root);
primaryStage.setTitle("Pablo!");
primaryStage.setScene(scene);
primaryStage.show();
playerViews = new PlayerView[]{ playerView1, playerView2, playerView3, playerView4 };
playerView1.init(playerName); // NullPointerException occurs at this line
...
}
}
Stack Trace
Exception in Application start method
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 com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
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.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$1(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.NullPointerException
at pablo.Board.start(Board.java:39)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$8(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$7(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$5(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$6(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
Is there anything wrong with the way that I am using the @FXML
annotation in Board.java to refer to my PlayerView
instances in Board.fxml? How should I be referring to them instead?
Define PlayerView
:
PlayerView.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<fx:root type="javafx.scene.layout.VBox" xmlns:fx="http://javafx.com/fxml">
<Label fx:id="nameLbl" alignment="CENTER" contentDisplay="CENTER" prefHeight="66.0" prefWidth="87.0" text="--" />
</fx:root>
PlayerView.java which also serves as controller:
public class PlayerView extends VBox {
@FXML Label nameLbl;
public PlayerView() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("PlayerView.fxml"));
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
public void init(String name) {
nameLbl.setText(name);
}
}
Board.fxml and its controller:
<?xml version="1.0" encoding="UTF-8"?>
<?import fx_tests.a_test.PlayerView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" prefHeight="400.0"
prefWidth="600.0" alignment="CENTER" fx:controller="foo.bar.BoardController">
<BorderPane>
<top>
<PlayerView fx:id="playerView2"/>
</top>
<bottom>
<PlayerView fx:id="playerView1"/>
</bottom>
<left>
<PlayerView fx:id="playerView3"/>
</left>
<right>
<PlayerView fx:id="playerView4"/>
</right>
</BorderPane>
</VBox>
Let the controller change Boaed
state as needed:
public class BoardController{
@FXML PlayerView playerView1, playerView2, playerView3, playerView4;
public PlayerView getBottom(){
return playerView1;
}
public PlayerView getTop(){
return playerView2;
}
public PlayerView getLeft(){
return playerView3;
}
public PlayerView getRight(){
return playerView4;
}
}
Get the controller in Board
and use it :
public class Board extends Application {
@FXML PlayerView playerView1, playerView2, playerView3, playerView4;
@Override
public void start(Stage primaryStage) throws IOException, ClassNotFoundException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Board.fxml"));
VBox root = loader.load();
BoardController controller = loader.getController();
Button btn = new Button("Add Names");
btn.setOnAction(e->{
controller.getTop().init("Top");
controller.getBottom().init("Bottom");
controller.getLeft().init("Left");
controller.getRight().init("Right");
});
root.getChildren().add(btn);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(null);
}
}