Reputation: 3018
I noticed a weird behaviour in one of my controller classes. One of the components is not getting initialized.
This is the fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="500.0" prefWidth="712.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="modulartoolkit.ConfigWindowController">
<children>
<Label layoutX="14.0" layoutY="12.0" text="Processes:" />
<Button fx:id="newProcessButton" layoutX="324.0" layoutY="82.0" mnemonicParsing="false" onAction="#handleNewProcessButtonAction" prefHeight="25.0" prefWidth="51.0" text="New" />
<Button fx:id="editProcessButton" layoutX="324.0" layoutY="118.0" mnemonicParsing="false" onAction="#handleEditProcessButtonAction" prefHeight="25.0" prefWidth="51.0" text="Edit" />
<Button fx:id="deleteProcessButton" layoutX="324.0" layoutY="154.0" mnemonicParsing="false" onAction="#handleDeleteProcessButtonAction" text="Delete" />
<ListView fx:id="processListView" layoutX="14.0" layoutY="30.0" prefHeight="200.0" prefWidth="295.0" />
<Label layoutX="14.0" layoutY="250.0" text="Available Modules:" />
<Label layoutX="400.0" layoutY="250.0" text="Configured Modules:" />
<ListView layoutX="397.0" layoutY="266.0" prefHeight="200.0" prefWidth="295.0" />
<TreeView layoutX="14.0" layoutY="266.0" prefHeight="200.0" prefWidth="295.0" />
</children>
</AnchorPane>
And this would be the controller:
public class ConfigWindowController implements Initializable {
private Database database;
private final ObservableList processListData = FXCollections.observableArrayList();
@FXML
private Button newProcessButton;
private Button editProcessButton;
private Button deleteProcessButton;
private ListView<String> processListView;
/**
* Initializes the controller class.
*
* @param url
* @param rb
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
this.database = Database.getInstance();
processListView.setItems(processListData);
}
@FXML
private void handleNewProcessButtonAction(ActionEvent event) {
System.out.println("new Process");
}
@FXML
private void handleEditProcessButtonAction(ActionEvent event) {
System.out.println("EDIT!");
}
@FXML
private void handleDeleteProcessButtonAction(ActionEvent event) {
System.out.println("DELETE!");
}
}
Once I start the application, I get a NullPointerException
because the ListView
is null.
I can fix it by adding an @FXML annotation to the ListView
:
@FXML
private Button newProcessButton;
private Button editProcessButton;
private Button deleteProcessButton;
@FXML
private ListView<String> processListView;
It also works if I move the ListView
to the top of the declaration. All the buttons are getting initialized in each case.
Does anybody know why this is happening? I thought the annotation only needs to be at the start of the declarations. Or does it have to be entered for each type of component?
Upvotes: 1
Views: 1888
Reputation: 49215
Actually the last two buttons are not initiated, so they must be null
. However the buttons defined in the fxml file are instantiated and have onAction
properties set with the ones defined in the controller class which also have @FXML annotated. Thus clicking on these buttons will work and triggers the event action handlers as expected.
@FXML annotation obligates the FXMLLoader to instantiate the annotated node and link it to the node defined in fxml file with the respective fx:id
. So if the node is not annotated with @FXML and not initiated explicitly then it is null. This is how the annotations work in Java language. You can confirm this by checking the last two buttons for nullness.
So, every control/node should be annotated separately:
@FXML
private Button newProcessButton;
@FXML
private Button editProcessButton;
@FXML
private ListView<String> processListView;
Or as combined like:
@FXML
private Button newProcessButton, editProcessButton;
@FXML
private ListView<String> processListView;
Upvotes: 1