So I'm learning how to use FXMl and I have run into yet another problem with getting a reference to an object by fx:id at runtime, I get a null pointer exception with the following code.
<TableView fx:id="productTable" layoutX="92.0" layoutY="319.0" prefHeight="97.0" prefWidth="363.0" GridPane.columnIndex="7" GridPane.columnSpan="5" GridPane.rowIndex="3" GridPane.rowSpan="2">
<columns>
<TableColumn prefWidth="98.0" text="Product ID" fx:id="productID"/>
<TableColumn prefWidth="110.0" text="Product Name" fx:id="productName"/>
<TableColumn prefWidth="131.0" text="Inventory Level" fx:id="productInventoryLevel"/>
<TableColumn prefWidth="128.0" text="Price per Unit" fx:id="productPrice"/>
</columns>
</TableView>
//Controller.java
@FXML private TableView<Product> productTable;
@FXML private TableColumn<Product, String> productID;
@FXML private TableColumn<Product, String> productPrice;
@FXML private TableColumn<Product, String> productName;
@FXML private TableColumn<Product, String> productInventoryLevel;
//Product properties declared the same as https://docs.oracle.com/javafx/2/ui_controls/table-view.htm#
private ObservableList<Product> productData = FXCollections.observableArrayList(
new Product(0, "Wheel", 100.99, 4, 0, 1),
new Product(1, "Seat", 50.0, 4, 0, 1));
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
System.out.println("Product ID factories");
productID.setCellValueFactory(new PropertyValueFactory<Product, String>("productID"));
productPrice.setCellValueFactory(new PropertyValueFactory<Product, String>("productPrice"));
productInventoryLevel.setCellValueFactory(new PropertyValueFactory<Product, String>("productInventoryLevel"));
productName.setCellValueFactory(new PropertyValueFactory<Product, String>("productName"));
productTable.setItems(productData);
System.out.println("Set items");
}
As you can see I declare the @FXML
tag, then the variable by fx:id
and right after my first print statement I get a runtime nullpointer exception on the productID
EDIT: The above code currently runs without error but will only populate the "productID" section of my table.
Firstly, it's not sure what the UI
class extends. It does look like it extended from Application
, and is the starting class of the whole application.
Making my best guess, I would say there are two possible reasons why you did this:
Application
extending) class. This is unlikely since you said you had a NullPointerException
, unless you manually called start()
.FXMLDocument.fxml
. This seems to be the likely case here.Assuming that you want the Application
extending class to be a controller, you need to create an instance of FXMLLoader
, instead of using the static method FXMLLoader.load()
.
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
loader.setController(this); // You need to set this instance as the controller.
Parent root = loader.load();
// Other stuff you need to do
productID.setCellValueFactory(new PropertyValueFactory<Product, String>("productID"));
This will set the currently running instance of UI
as the controller for FXMLDocument.fxml
.
There are several points to take note.
fx:controller
in FXML, or create a new instance of UI
(and setting it) as an alternative. You must provide the current instance to the FXMLLoader
.@FXML
will only be injected (i.e. non-null) after FXMLLoader.load()
has been called, or in initialize()
method. If you try to call productID.setCellValueFactory(new PropertyValueFactory<Product, String>("productID"));
, you may get a NullPointerException
, depending on the sequence of your codes.Lastly, you do have an option of making another class the controller of the FXML file. In fact, most people would do that as that is the purpose of MVC (which is the architecture JavaFX uses). Of course, all injected fields and initialization would also move to the new controller class. Also remember that injected fields are null
in constructor - so initialize in initialize()
of the new controller class.