Reputation: 55
I am having a huge problem with TableView control in JavaFX. That's the first time I'm ever using it and I don't know what am I doing wrong.
My code:
Main class:
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller class:
package sample;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TableView;
import javafx.stage.Modality;
import javafx.stage.Stage;
import java.io.IOException;
public class Controller {
@FXML
private TableView<Person> peopleTableView;
@FXML
public void openNewWindow(){
try {
Parent root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
Stage mainWindow = new Stage();
mainWindow.setTitle("App");
mainWindow.setScene(new Scene(root, 1200, 600));
mainWindow.initModality(Modality.APPLICATION_MODAL);
mainWindow.show();
ObservableList<Person> people = FXCollections.observableArrayList();
people.add(new Person("Walter", "White"));
people.add(new Person("Gus", "Fring"));
peopleTableView.setItems(people);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Person class:
package sample;
import javafx.beans.property.SimpleStringProperty;
public class Person {
private SimpleStringProperty firstName = new SimpleStringProperty("");
private SimpleStringProperty lastName = new SimpleStringProperty("");
public Person(String firstName, String lastName) {
this.firstName.set(firstName);
this.lastName.set(lastName);
}
public String getFirstName() {
return firstName.get();
}
public SimpleStringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
return lastName.get();
}
public SimpleStringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
}
And two FXMLs codes, one for the window that shows up when we run the code:
sample.fxml:
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml">
<center>
<Button fx:id="openWindowButton" text="Open new window" onAction="#openNewWindow"/>
</center>
</BorderPane>
And one for the new stage with TableView:
mainWindow.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<center>
<TableView fx:id="peopleTableView">
<columns>
<TableColumn text="First Name">
<cellValueFactory>
<PropertyValueFactory property="firstName"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Last Name">
<cellValueFactory>
<PropertyValueFactory property="lastName"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</center>
</BorderPane>
So the issue is - I can't set items of TableView, because I get an exception that it is null even though it clearly shows up in the new stage which (I believe) it actually exists.
Error code:
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8879)
at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3851)
at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1200(Scene.java:3579)
at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1849)
at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2588)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:390)
at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:273)
at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1784)
... 47 more
Caused by: java.lang.NullPointerException
at TableView.issue/sample.Controller.openNewWindow(Controller.java:35)
... 59 more
Upvotes: 3
Views: 941
Reputation: 209684
The problem is you're trying to use the same controller class for both FXML files (I think, assuming that this means you'll have the same controller instance both times, which is not the case).
The openNewWindow()
method is called on the Controller
instance that is created when you load sample.fxml
. That FXML file has no field called peopleTableView
, so peopleTableView
is null in that instance, and peopleTableView.setItems(...)
results in a null pointer exception.
The peopleTableView
field is initialized in the Controller
instance that is created when you load mainWindow.fxml
, but no methods are called on that instance.
The solution is to use a different controller class for each FXML file (you should basically always do this). You can use the initialize()
method in the controller for mainWindow.fxml
to initialize the table data.
Here's the controller for sample.fxml
:
public class LoginController {
@FXML
public void openNewWindow(){
try {
Parent root = FXMLLoader.load(getClass().getResource("mainWindow.fxml"));
Stage mainWindow = new Stage();
mainWindow.setTitle("App");
mainWindow.setScene(new Scene(root, 1200, 600));
mainWindow.initModality(Modality.APPLICATION_MODAL);
mainWindow.show();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
And here's the controller for mainWindow.fxml
:
public class MainController {
@FXML
private TableView<Person> peopleTableView ;
@FXML
private void initialize() {
ObservableList<Person> people = FXCollections.observableArrayList();
people.add(new Person("Walter", "White"));
people.add(new Person("Gus", "Fring"));
peopleTableView.setItems(people);
}
}
Now just update the fx:controller
attributes in both FXML files.
If you need to pass data from one controller to the next, see Passing Parameters JavaFX FXML
For example, if you wanted to load the table data in the first controller's openNewWindow()
method (which seems unnatural, but will serve as an example), you could do:
public class LoginController {
@FXML
public void openNewWindow(){
try {
// Note using FXMLLoader instance, not static load(URL) method:
FXMLLoader loader = new FXMLLoader(getClass().getResource("mainWindow.fxml"));
Parent root = loader.load();
Stage mainWindow = new Stage();
mainWindow.setTitle("App");
mainWindow.setScene(new Scene(root, 1200, 600));
mainWindow.initModality(Modality.APPLICATION_MODAL);
mainWindow.show();
ObservableList<Person> people = FXCollections.observableArrayList();
people.add(new Person("Walter", "White"));
people.add(new Person("Gus", "Fring"));
MainController mainController = loader.getController();
mainController.initializeTableData(people);
}
catch (IOException e) {
e.printStackTrace();
}
}
}
and
public class MainController {
@FXML
private TableView<Person> peopleTableView ;
public void initializeTableData(ObservableList<Person> people) {
peopleTableView.setItems(people);
}
}
But again, for a full discussion of the various ways to communicate between controllers, see Passing Parameters JavaFX FXML
Upvotes: 5