1

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?

1 Answer 1

1

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;
Sign up to request clarification or add additional context in comments.

1 Comment

That makes sense, they are not null though. The buttons work just fine and in all my other JavaFX applications I only have one annotation, with dozens of components. I guess I did it wrong all the time and didn't notice because it worked until now. I wonder why it worked though.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.