javafx.fxml.LoadException 尝试在 TableView JavaFX 中显示对象列表时

javafx.fxml.LoadException when trying to display a list of objects in a TableView JavaFX

我正在尝试在 JavaFX table 中显示对象列表,但在遍历要添加到 table 的对象时,我将 运行 保留在 JavaFX 异常中,确实任何人都知道可能是什么原因造成的,我已经尝试了 ObservableLists 的几种不同实现,但它们都看到产生相同的结果,我的结论是 class 或 .fxml 文件可能存在错误。提前致谢。

JavaFX:

    <?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="HomePageController">
   <left>
      <AnchorPane prefHeight="400.0" prefWidth="200.0" style="-fx-background-color: #c7a4ff;" BorderPane.alignment="CENTER">
         <children>
            <Label fx:id="userLabel" alignment="TOP_CENTER" contentDisplay="CENTER" text="Hello &lt;User&gt;" textAlignment="CENTER" textFill="WHITE">
               <font>
                  <Font name="Segoe UI Semilight" size="24.0" />
               </font>
               <padding>
                  <Insets bottom="10.0" left="25.0" right="25.0" top="10.0" />
               </padding>
            </Label>
               <padding>
                  <Insets bottom="10.0" left="25.0" right="25.0" top="10.0" />
               </padding>
            </Label>
            <Button layoutY="60.0" mnemonicParsing="false" onAction="#MenuSwitch" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #aa80ff;" text="Home" textFill="WHITE">
               <font>
                  <Font size="14.0" />
               </font>
            </Button>
            <Button layoutY="120.0" mnemonicParsing="false" onAction="#MenuSwitch" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #aa80ff;" text="Diet" textFill="WHITE">
               <font>
                  <Font size="14.0" />
               </font>
            </Button>
            <Button layoutY="180.0" mnemonicParsing="false" onAction="#MenuSwitch" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #aa80ff;" text="Workout" textFill="WHITE">
               <font>
                  <Font size="14.0" />
               </font>
            </Button>
            <Button layoutY="240.0" mnemonicParsing="false" onAction="#MenuSwitch" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #aa80ff;" text="Groups" textFill="WHITE">
               <font>
                  <Font size="14.0" />
               </font>
            </Button>
            <Button layoutY="300.0" mnemonicParsing="false" onAction="#MenuSwitch" prefHeight="50.0" prefWidth="200.0" style="-fx-background-color: #aa80ff;" text="User Information" textFill="WHITE">
               <font>
                  <Font size="14.0" />
               </font>
            </Button>
         </children>
      </AnchorPane>
   </left>
   <right>
      <AnchorPane prefHeight="400.0" prefWidth="394.0" BorderPane.alignment="CENTER">
         <children>
            <Label alignment="TOP_CENTER" contentDisplay="CENTER" layoutX="148.0" text="Home" textAlignment="CENTER" textFill="#aa80ff">
               <font>
                  <Font name="Segoe UI Semilight" size="24.0" />
               </font>
               <padding>
                  <Insets bottom="10.0" left="10.0" right="25.0" top="10.0" />
               </padding>
            </Label>
            <Label alignment="TOP_CENTER" contentDisplay="CENTER" layoutX="14.0" layoutY="55.0" text="My Goals" textAlignment="CENTER" textFill="#aa80ff">
               <font>
                  <Font name="Segoe UI Semilight" size="18.0" />
               </font>
               <padding>
                  <Insets bottom="10.0" left="10.0" right="25.0" top="10.0" />
               </padding>
            </Label>
            <TableView fx:id="tbData" layoutX="14.0" layoutY="102.0" prefHeight="200.0" prefWidth="360.0" style="-fx-background-color: #ffffff;">
              <columns>
                 <TableColumn fx:id="goalName" prefWidth="160.0" text="Name" />
                 <TableColumn fx:id="goalDate" prefWidth="50.0" text="Date" />
                 <TableColumn fx:id="goalStatus" prefWidth="75.0" text="Status" />
                 <TableColumn fx:id="goalGroups" prefWidth="10.0" text="Groups"/>
              </columns>
            </TableView>
         </children>
      </AnchorPane>
   </right>
   <bottom>
      <AnchorPane prefHeight="50.0" prefWidth="600.0" style="-fx-background-color: #c7a4ff;" BorderPane.alignment="CENTER">
         <children>
            <ImageView fitHeight="30.0" fitWidth="30.0" layoutX="380.0" layoutY="10.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@img/add_box.png" />
               </image>
            </ImageView>
            <Button graphicTextGap="0.0" layoutX="380.0" layoutY="10.0" mnemonicParsing="false" onAction="#openSelectGoalTypePage" opacity="0.0" prefHeight="30.0" prefWidth="30.0" />
            <ImageView fitHeight="30.0" fitWidth="30.0" layoutX="300.0" layoutY="10.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@img/mode_edit.png" />
               </image>
            </ImageView>
            <Button graphicTextGap="0.0" layoutX="300.0" layoutY="10.0" mnemonicParsing="false" onAction="#openEditGoalPage" opacity="0.0" prefHeight="30.0" prefWidth="30.0" />
            <ImageView fitHeight="30.0" fitWidth="30.0" layoutX="460.0" layoutY="10.0" pickOnBounds="true" preserveRatio="true">
               <image>
                  <Image url="@img/delete.png" />
               </image>
            </ImageView>
            <Button graphicTextGap="0.0" layoutX="460.0" layoutY="10.0" mnemonicParsing="false" onAction="#removeTableItem" opacity="0.0" prefHeight="30.0" prefWidth="30.0" />
         </children>
      </AnchorPane>
   </bottom>
</BorderPane>

控制者:

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

public class HomePageController extends BaseController implements Initializable {

    @FXML
    private TableView<Goal> tbData = new TableView<>();

    @FXML
    public TableColumn<Goal, String> goalName;

    @FXML
    public TableColumn<Goal, String> goalDate;

    @FXML
    public TableColumn<Goal, String> goalStatus;

    @FXML
    public TableColumn<Goal, String> goalGroups;

    /*@FXML
    private Label userLabel;*/

    //allow user to select a table item/row and delete it using the delete button
    public void removeTableItem(){

    }

    public void openSelectGoalTypePage(javafx.event.ActionEvent actionEvent) throws IOException {
        root = FXMLLoader.load(getClass().getResource("SelectGoalType.fxml"));
        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public void openAddGoalPage(javafx.event.ActionEvent actionEvent) throws IOException {
        //if user selects weight goal then open AddWeightGoal page
        //else if user selects workout goal then open AddWorkoutGoal page
        root = FXMLLoader.load(getClass().getResource("AddWeightGoal.fxml"));
        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public void openEditGoalPage(javafx.event.ActionEvent actionEvent) throws IOException {
        root = FXMLLoader.load(getClass().getResource("EditGoal.fxml"));
        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {



        tbData = new TableView<>();
        TableColumn<Goal, String> goalName = new TableColumn<>("GoalName");
        goalName.setCellValueFactory(new PropertyValueFactory<>("goalName"));
        TableColumn<Goal, String> goalDate = new TableColumn<>("GoalDate");
        goalDate.setCellValueFactory(new PropertyValueFactory<>("goalDate"));
        TableColumn<Goal, String> goalStatus = new TableColumn<>("GoalStatus");
        goalDate.setCellValueFactory(new PropertyValueFactory<>("goalStatus"));
        TableColumn<Goal, String> goalGroups = new TableColumn<>("GoalGroups");
        goalGroups.setCellValueFactory(new PropertyValueFactory<>("goalGroups"));
        tbData.getColumns().add(goalName);
        tbData.getColumns().add(goalDate);
        tbData.getColumns().add(goalStatus);
        tbData.getColumns().add(goalGroups);

        tbData.setItems(getGoals());

    }


    private ObservableList<Goal> getGoals(){
        ObservableList<Goal> goals = FXCollections.observableArrayList();
        for(int i = 0; i < user.getGoals().size(); i++){
            goals.add(user.getGoals().get(i));
        }
        return goals;
    }
}

目标Class:

import java.io.Serial;
import java.io.Serializable;

public class Goal implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    public String getGoalName() {
        return goalName;
    }

    public void setGoalName(String goalName) {
        this.goalName = goalName;
    }



    public String getGoalDate() {
        return goalDate;
    }

    public void setGoalDate(String goalDate) {
        this.goalDate = goalDate;
    }

    public String getGoalStatus() {
        return goalStatus;
    }

    public void setGoalStatus(String goalStatus) {
        this.goalStatus = goalStatus;
    }

    public String getGoalGroups() {
        return goalGroups;
    }

    public void setGoalGroups(String goalGroups) {
        this.goalGroups = goalGroups;
    }

    public Goal(String goalName, String goalDate, String goalStatus, String goalGroups) {
        this.goalName = goalName;
        this.goalDate = goalDate;
        this.goalStatus = goalStatus;
        this.goalGroups = goalGroups;
    }

    String goalName;
    String goalDate;
    String goalStatus;
    String goalGroups;
} 

编辑:

我做了更多的调试,当我用一个 try-catch 块包围它时,它似乎从基本控制器指向我的用户对象为 null,尽管我相信我将它传递给了控制器持有用户对象的基本控制器,以及我用来传递用户对象的调用(在作为参数传递之前确认用户没有问题)。我似乎得到的唯一堆栈跟踪是加载程序,这是来自 try catch:java.lang.NullPointerException: Cannot invoke "User.getGoals()" because "this.user" is null :

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;

import java.io.IOException;

public class BaseController {

    User user;

    Parent root;
    Stage stage;
    Scene scene;
    @FXML
    Label userLabel;

    //default constructor
    public BaseController(){

    }

    void setUser(User user){
        this.user = user;
        userLabel.setText(user.getUsername());
    }

    //connect to current user once logged in
    /*
    public BaseController(User user){
        displayUsername(User);
    }*/

    //display current logged in user's first name
    /*
    private void displayUsername(User user){
        userLabel.setText(user.firstName);
    }*/

    /**
     * Menu Functions just in case
     */
//    public void openHomePage(javafx.event.ActionEvent actionEvent) throws IOException {
//        root = FXMLLoader.load(getClass().getResource("HomePage.fxml"));
//        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
//        scene = new Scene(root);
//        stage.setScene(scene);
//        stage.show();
//    }
//
//    public void openDietPage(javafx.event.ActionEvent actionEvent) throws IOException {
//        root = FXMLLoader.load(getClass().getResource("DietPage.fxml"));
//        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
//        scene = new Scene(root);
//        stage.setScene(scene);
//        stage.show();
//    }
//
//    public void openWorkoutPage(javafx.event.ActionEvent actionEvent) throws IOException {
//        root = FXMLLoader.load(getClass().getResource("WorkoutPage.fxml"));
//        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
//        scene = new Scene(root);
//        stage.setScene(scene);
//        stage.show();
//    }
//
//    public void openGroupsPage(javafx.event.ActionEvent actionEvent) throws IOException {
//        root = FXMLLoader.load(getClass().getResource("GroupsPage.fxml"));
//        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
//        scene = new Scene(root);
//        stage.setScene(scene);
//        stage.show();
//    }
//
//    public void openUserPage(javafx.event.ActionEvent actionEvent) throws IOException {
//        root = FXMLLoader.load(getClass().getResource("UserPage.fxml"));
//        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
//        scene = new Scene(root);
//        stage.setScene(scene);
//        stage.show();
//    }

    public void MenuSwitch(javafx.event.ActionEvent actionEvent) throws IOException{
        String text = ((Button)actionEvent.getSource()).getText();
        String filename="";
        switch(text){
            case "Home":
                filename="HomePage.fxml";break;
            case "Diet":
                filename="DietPage.fxml";break;
            case "Workout":
                filename="WorkoutPage.fxml";break;
            case "Groups":
                filename="GroupsPage.fxml";break;
            case "User Information":
                filename="UserPage.fxml";break;
            case "Edit group":
                filename="EditGroupPage.fxml"; break;
            case "Create group":
                filename="CreateGroupPage.fxml";break;
            case "Add Diet Item":
                filename="AddDietItemPage.fxml"; break;
            case "EditWorkoutPage":
                filename="EditWorkoutPage.fxml";break;
            case "AddWorkoutPage":
                filename="AddWorkoutPage.fxml";break;
            case "EditUserPage":
                filename="EditUserPage.fxml";break;
        }
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(filename));

        Parent root = (Parent) fxmlLoader.load();

        BaseController baseController = fxmlLoader.getController();

        System.out.println(user);

        baseController.setUser(user);

        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();

    }
}

来自传递用户对象的前一个控制器:

FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("HomePage.fxml"));

                    Parent root = (Parent) fxmlLoader.load();

                    HomePageController controller = fxmlLoader.getController();

                    controller.setUser(newUser);

                    stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
                    scene = new Scene(root);
                    stage.setScene(scene);
                    stage.show();

谢谢!

编辑二: 完整堆栈跟踪:

java.lang.NullPointerException: Cannot invoke "User.getGoals()" because "this.user" is null
java.lang.NullPointerException: Cannot invoke "User.getGoals()" because "this.user" is null
    at HomePageController.initialize(HomePageController.java:75)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2655)
    at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
    at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:2517)
    at UserLoginController.login(UserLoginController.java:108)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    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:1857)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1729)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    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:8886)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:203)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:208)
    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:247)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)
    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:3856)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1851)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2584)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:409)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:299)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent(GlassViewEventHandler.java:447)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:412)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:446)
    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.gtk.GtkApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop(GtkApplication.java:277)
    at java.base/java.lang.Thread.run(Thread.java:832)
java.sql.SQLException: The prepared statement has been finalized

控制器的initialize()方法在FXMLLoader.load()的执行过程中被调用,所以initialize(),因此getGoals()之前被调用[=40] =] 你打电话给 setUser(...)。因此 user 在您尝试调用 user.getGoals().

时为空

要在您设置的结构中修复此问题,您需要在调用 setUser(...) 时填充 table 列表。您可以在子类中使用重写的 setUser(...) 方法来做到这一点。

另请注意,初始化 @FXML 注释字段总是错误的(因为它们由 FXMLLoader 初始化为 FXML 文件中定义的元素)。所以你的控制器应该看起来像这样:

public class HomePageController extends BaseController implements Initializable {

    @FXML
    private TableView<Goal> tbData ;

    @FXML
    public TableColumn<Goal, String> goalName;

    @FXML
    public TableColumn<Goal, String> goalDate;

    @FXML
    public TableColumn<Goal, String> goalStatus;

    @FXML
    public TableColumn<Goal, String> goalGroups;

    /*@FXML
    private Label userLabel;*/

    //allow user to select a table item/row and delete it using the delete button
    public void removeTableItem(){

    }

    public void openSelectGoalTypePage(javafx.event.ActionEvent actionEvent) throws IOException {
        root = FXMLLoader.load(getClass().getResource("SelectGoalType.fxml"));
        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public void openAddGoalPage(javafx.event.ActionEvent actionEvent) throws IOException {
        //if user selects weight goal then open AddWeightGoal page
        //else if user selects workout goal then open AddWorkoutGoal page
        root = FXMLLoader.load(getClass().getResource("AddWeightGoal.fxml"));
        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    public void openEditGoalPage(javafx.event.ActionEvent actionEvent) throws IOException {
        root = FXMLLoader.load(getClass().getResource("EditGoal.fxml"));
        stage = (Stage)((Node)actionEvent.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setScene(scene);
        stage.show();
    }

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {



        goalName.setCellValueFactory(new PropertyValueFactory<>("goalName"));
        goalDate.setCellValueFactory(new PropertyValueFactory<>("goalDate"));
        goalDate.setCellValueFactory(new PropertyValueFactory<>("goalStatus"));
        goalGroups.setCellValueFactory(new PropertyValueFactory<>("goalGroups"));


    }

    @Override
    void setUser(User user){
        super.setUser(user);
        tbData.setItems(getGoals());
    }

    private ObservableList<Goal> getGoals(){
        return FXCollections.observableList(user.getGoals());
    }
}

一种更标准的方法是使用 MVC 类型的设计,其中用户由 ObjectProperty<User> 表示,您可以观察它的变化。例如

public class BaseController {

    private final ObjectProperty<User> user;

    // ...

    //default constructor
    public BaseController(){
        user = new SimpleObjectProperty();
        user.addListener((obs, oldUser, newUser) ->
            userLabel.setText(newUser == null ? "" : newUser.getUsername()));
    }

    public ObjectProperty<User> userProperty() {
        return user ;
    }

    public final User getUser() {
        return userProperty().get();
    }

    public final void setUser(User user) {
        userProperty().set(user);
    }

    // ...
}

public class HomePageController extends BaseController implements Initializable {

    @FXML
    private TableView<Goal> tbData ;

    @FXML
    public TableColumn<Goal, String> goalName;

    @FXML
    public TableColumn<Goal, String> goalDate;

    @FXML
    public TableColumn<Goal, String> goalStatus;

    @FXML
    public TableColumn<Goal, String> goalGroups;

    public HomePageController() {
        userProperty().addListener((obs, oldUser, newUser) -> {
            if (newUser == null) {
                tbData.setItems(FXCollections.observableArrayList());
            } else {
                tbData.setItems(getGoals());
            }
        });
    }

    // ...

    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        goalName.setCellValueFactory(new PropertyValueFactory<>("goalName"));
        goalDate.setCellValueFactory(new PropertyValueFactory<>("goalDate"));
        goalDate.setCellValueFactory(new PropertyValueFactory<>("goalStatus"));
        goalGroups.setCellValueFactory(new PropertyValueFactory<>("goalGroups"));


    }

    

    private ObservableList<Goal> getGoals(){
        return FXCollections.observableList(user.getGoals());
    }
}

通常这里 user 不是您需要观察的唯一值,因此它会被分解到某个模型中。例如,参见