JAVAFX:控制器 class 中的字段值未保存 correctly/trouble 更新列表视图?

JAVAFX: Field value in controller class isn't saved correctly/trouble updating listview?

所以我正在用JavaFX制作一个小程序来熟悉它。

我很确定我对 javafx 的工作原理有一些基本的了解,但我似乎无法理解,因为我遇到了一些我找不到解决方案的问题。尽管在 google.

上对它们进行了彻底研究

目前我有一个使用 eclipse 和 scenebuilder 1.1 的设置(因为 2.0 给我带来了各种麻烦)。

我有一个非常简单的设置,主应用程序加载初始主应用程序 window。

public class Main extends Application {
        @Override 
        public void start(final Stage stage) throws Exception { 
           Parent root = FXMLLoader.load(getClass().getResource("/application/ApplicationWindow.fxml"));        
            Scene scene = new Scene(root);
            stage.getIcons().add(new Image("/application/bug3.png"));
            stage.setResizable(true);
            stage.setTitle("Simple Bugtracker");
            stage.setMinHeight(500);
            stage.setMinWidth(800);
            stage.setScene(scene);
            stage.show();
        }        

        public static void main(String[] args) { launch(args); } 
}

它似乎工作正常。然后我创建了一个控制器 class,它包含正确的 fx:id'et gui 元素作为字段值,它工作正常,我可以用 java 代码来操作它们。

不过到目前为止我遇到了两个问题。

1) 当我从我的一个列表视图中删除某些内容时,我设法解决了更新问题,因此它通过添加一个 if null 场景立即将其从列表视图中删除。但是当我创建我的对象类型的新实例时,列表视图直到我重新启动程序才更新?

2) 其次,我在控制器 class 中遇到了一个简单的 int 字段值问题,我想在即时调用的方法中使用它的当前值(在 scenebuilder 中的按钮上设置),但是它似乎总是坚持认为即使值设置正确,它仍然是启动期间实例化时的原始值。

这是我的相当大的控制器 class。

public class MyController implements Initializable {

    // Logic related fields
    public ProjectList<Project> projectList = new ProjectList<Project>();
    public int currentProjectIndex = -1;
    public Bug currentBug;

    // Listview buffers
    public ObservableList<Project> projectsListBuffer = FXCollections.observableArrayList();
    public ObservableList<Bug> unsolvedListBuffer = FXCollections.observableArrayList();
    public ObservableList<Bug> solvedListBuffer = FXCollections.observableArrayList();

    // GUI related fields

    @FXML public ListView<Project> projectsListView;
    @FXML public ListView<Bug> unsolvedListView;
    @FXML public ListView<Bug> solvedListView;


    @FXML public TextArea topDisplayArea;
    @FXML public Button btnCreateProject;

    @FXML public TextField titleFieldCreateProject;
    @FXML public TextArea descriptionAreaCreateProject;

    @FXML public AnchorPane createProjectWindow;
    @FXML public AnchorPane projectsListViewAnchor;

    @FXML public Label projectTitleLabel;
    @FXML public Button createBugButton;

    @FXML public TextField titleFieldCreateBug;
    @FXML public TextArea descriptionAreaCreateBug;

    public Stage createProjectStage;
//  public Stage createBugStage;


    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        // load data from file system
        try {
            loadData();
        } catch (IOException e) {           
            e.printStackTrace();
        }
        // Instantiate gui field values
//      projectsListView = new ListView();
//      unsolvedListView = new ListView();
//      solvedListView = new ListView();
//
//
//      topDisplayArea = new TextArea();
//      btnCreateProject = new Button();
//
//      titleFieldCreateProject = new TextField();
//      descriptionAreaCreateProject = new TextArea();
//
//      createProjectWindow = new AnchorPane();
//      projectsListViewAnchor = new AnchorPane();
//
//      projectTitleLabel = new Label();
//      createBugButton = new Button();
//      
//      titleFieldCreateBug = new TextField();
//      descriptionAreaCreateBug = new TextArea();


        updateListBuffers();

        projectsListView.setPrefHeight( getScreenH() );
        projectsListView.setItems(projectsListBuffer);
        unsolvedListView.setPrefHeight( getScreenH() );
        unsolvedListView.setItems(unsolvedListBuffer);

        // settings on necessary gui items

        topDisplayArea.setPrefHeight( getScreenH() );
        topDisplayArea.setPrefWidth( getScreenW() );
        topDisplayArea.setEditable(false);
        topDisplayArea.setBackground(null); // something to be done here for transparent logo in background of all project descriptions etc.

        // ved dobbeltklik i projektlisten, vælg project og sæt titel
        projectsListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent click) {

                if (click.getClickCount() == 2) {
                    //Use ListView's getSelected Item
                    currentProjectIndex = projectsListView.getSelectionModel().getSelectedIndex();
                    Project currentProject = projectList.get(projectsListView.getSelectionModel().getSelectedIndex());
                    projectTitleLabel.setText(currentProject.getTitle());
                    System.out.println( "Selected project:"+currentProject.getTitle());                 
                    solvedListView.setItems(unsolvedListBuffer);
                    for (Bug b : currentProject.solvedBugs) {
                        System.out.println(b.getTitle()+"\n"+b.getErrorDescription());
                    }
                    updateListBuffers();
                }
            }
        });

        // ved dobbeltklik i uløste bugs listen, sæt top display område
        unsolvedListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent click) {
                if (click.getClickCount() == 2) {
                    //Use ListView's getSelected Item
                    currentBug = unsolvedListView.getSelectionModel().getSelectedItem();
                    topDisplayArea.setText(currentBug.getErrorDescription());
                }
            }
        });

        solvedListView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent click) {
                if (click.getClickCount() == 2) {
                    //Use ListView's getSelected Item
                    currentBug = solvedListView.getSelectionModel().getSelectedItem();
                    topDisplayArea.setText(currentBug.getErrorDescription());
                }
            }
        });

        // custom listview cell for the projects listview
        projectsListView.setCellFactory(new Callback<ListView<Project>, ListCell<Project>>() {
            @Override
            public ListCell<Project> call(ListView<Project> p) {

                ListCell<Project> cell = new ListCell<Project>() {
                    @Override
                    protected void updateItem(Project t, boolean bln) {

                        super.updateItem(t, bln);

                        if (t != null) {
                            // program a custom cell with several items in it
                            Text text = new Text();
                            Text text2 = new Text();
                            text.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
                            text.setText(t.getTitle());
                            text2.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
                            text2.setText(t.getCreationDate().toLocaleString());
                            text.setFont(Font.font ("Verdana", 12));
                            text2.setFont(Font.font ("Verdana", 10));
                            text.setFill(Color.BLACK);
                            text2.setFill(Color.TEAL);
                            AnchorPane pane = new AnchorPane();     
                            pane.setPrefHeight(90);
                            pane.setPrefWidth(300);
                            ImageView imageView = new ImageView();
                            imageView.setImage(new Image("/application/project1.png"));
                            imageView.setFitHeight(60);
                            imageView.setFitWidth(60);
                            Image imageOk = new Image(getClass().getResourceAsStream("/application/selectionicon.png"));

                            Button deleteButton = new Button("Delete");
                            deleteButton.setLayoutX(233);
                            deleteButton.setLayoutY(60);
                            text.setLayoutX(70);
                            text.setLayoutY(30);
                            text2.setLayoutX(70);
                            text2.setLayoutY(50);
                            pane.getChildren().addAll(imageView, text, text2, deleteButton);

                            // delete actionevent           
                            deleteButton.setOnAction(new EventHandler<ActionEvent>() {
                                @Override 
                                public void handle(ActionEvent e) { 

                                    Alert alert = new Alert(AlertType.CONFIRMATION);
                                    alert.setTitle("Confirm deletion");
                                    alert.setHeaderText("Are you sure you want to delete this project?");
                                    Optional<ButtonType> result = alert.showAndWait();
                                    if (result.get() == ButtonType.OK){
                                        int selIndex = projectsListView.getSelectionModel().getSelectedIndex();
                                        if (selIndex>-1) {
                                            projectsListBuffer.remove(selIndex);
                                            projectList.remove(selIndex);                           
                                            projectsListView.setItems(projectsListBuffer);
                                            projectList.save();
                                        }

                                    } else {
                                        System.out.println("DENIED!!");
                                    }                                                               
                                }
                            });

                            setPrefWidth(0);
                            setGraphic(pane);
                        } else {
                            setText(null);
                            setGraphic(null);
                        }
                    }
                };
                return cell;
            }
        });

        // custom listview cell for the unsolved bugs listview
        unsolvedListView.setCellFactory(new Callback<ListView<Bug>, ListCell<Bug>>() {
            @Override
            public ListCell<Bug> call(ListView<Bug> p) {

                ListCell<Bug> cell = new ListCell<Bug>() {
                    @Override
                    protected void updateItem(Bug t, boolean bln) {
                        super.updateItem(t, bln);

                        if (t != null) {
                            // program a custom cell with several items in it
                            Text text = new Text();
                            Text text2 = new Text();
                            text.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
                            text.setText(t.getTitle());
                            text2.wrappingWidthProperty().bind(p.widthProperty().subtract(15));
                            text2.setText(t.getCreationDate().toLocaleString());
                            text.setFont(Font.font ("Verdana", 12));
                            text2.setFont(Font.font ("Verdana", 10));
                            text.setFill(Color.BLACK);
                            text2.setFill(Color.TEAL);
                            AnchorPane pane = new AnchorPane();     
                            pane.setPrefHeight(90);
                            pane.setPrefWidth(300);
                            ImageView imageView = new ImageView();
                            imageView.setImage(new Image("/application/bug3.png"));
                            imageView.setFitHeight(60);
                            imageView.setFitWidth(60);
                            Image imageOk = new Image(getClass().getResourceAsStream("/application/selectionicon.png"));

                            Button selectButton = new Button("Select"); 
                            Button deleteButton = new Button("Delete");
                            selectButton.setLayoutX(233);
                            selectButton.setLayoutY(60);
                            deleteButton.setLayoutX(180);
                            deleteButton.setLayoutY(60);
                            text.setLayoutX(70);
                            text.setLayoutY(30);
                            text2.setLayoutX(70);
                            text2.setLayoutY(50);
                            pane.getChildren().addAll(imageView, text, text2);

                            deleteButton.setOnAction(new EventHandler<ActionEvent>() {
                                @Override 
                                public void handle(ActionEvent e) {                          
                                    projectsListBuffer.remove(projectsListView.getSelectionModel().getSelectedIndex());
                                    projectList.remove(projectsListView.getSelectionModel().getSelectedIndex());                            
                                    projectsListView.setItems(projectsListBuffer);
                                    projectList.save();
                                }
                            });

                            setPrefWidth(0);
                            setGraphic(pane);
                        } else {
                            setText(null);
                            setGraphic(null);
                        }
                    }
                };

                return cell;
            }
        });


    }

    // loads and show the create project window from it's fxml file (CreateProjectWindow.fxml)
    public void showCreateProjectWindow() {
        try {
            Parent root1 = FXMLLoader.load(getClass().getResource("/application/CreateProjectWindow.fxml"));    
            createProjectStage = new Stage();
            Scene scene = new Scene(root1);         
            createProjectStage.setScene(scene);  
            createProjectStage.setMinWidth(374);
            createProjectStage.setMinHeight(416);
            createProjectStage.setResizable(false);
            createProjectStage.show();          
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    // actionevent method that creates a new project   MANGLER AT FIKSE DET SÅ LISTVIEWET OPDATERER SIG MED DET SAMME
    public void createProject() {
        String title = titleFieldCreateProject.getText();
        String description = descriptionAreaCreateProject.getText();
        Project project = new Project(title, description, new Date());
        projectList.add(project);
        projectsListBuffer.add(project);
        projectList.save();
        updateListBuffers();
        projectsListView.setItems(projectsListBuffer);      

    }

    // loads and show the create project window from it's fxml file (CreateProjectWindow.fxml)
    public void showCreateBugWindow() {
        try {
            Parent root1 = FXMLLoader.load(getClass().getResource("/application/CreateBugWindow.fxml"));    
            createProjectStage = new Stage();
            Scene scene = new Scene(root1);         
            createProjectStage.setScene(scene);  
            createProjectStage.setMinWidth(374);
            createProjectStage.setMinHeight(416);
            createProjectStage.setResizable(false);
            createProjectStage.show();          
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    // actionevent method that creates a new bug   MANGLER AT FIKSE DET SÅ LISTVIEWET OPDATERER SIG MED DET SAMME
        public void createBug() {
            if (currentProjectIndex >-1) {
                String title = titleFieldCreateBug.getText();
                String description = descriptionAreaCreateBug.getText();
                System.out.println(title+"\n"+description);
                Bug bug = new Bug(title, description, new Date());
                projectList.get(currentProjectIndex).unsolvedBugs.add(bug);
                unsolvedListBuffer.add(bug);
                projectList.save();         
                updateListBuffers();
            } else {
                System.out.println("Failed creation.");
            }
        }

    // load, clear and reload buffers
    public void updateListBuffers() {
        if (currentProjectIndex>-1) {
            unsolvedListBuffer.clear();     
            for (int i=0; i<projectList.get(currentProjectIndex).unsolvedBugs.size(); i++) {            
                unsolvedListBuffer.add( projectList.get(currentProjectIndex).unsolvedBugs.get(i) );         
            }
            solvedListBuffer.clear();           
            for (int i=0; i<projectList.get(currentProjectIndex).solvedBugs.size(); i++) {          
                solvedListBuffer.add( projectList.get(currentProjectIndex).solvedBugs.get(i) );         
            }
//          unsolvedListView.setItems(null);
//          solvedListView.setItems(null);
            unsolvedListView.setItems(unsolvedListBuffer);
            solvedListView.setItems(solvedListBuffer);
        }   
        projectsListBuffer.clear();     
        for (int i=0; i<projectList.size(); i++) {          
            projectsListBuffer.add( projectList.get(i) );           
        }   
//      projectsListView.setItems(null);
        projectsListView.setItems(projectsListBuffer);

    }

    // file system related methods

    public void loadData() throws IOException {
        File fil = new File("Project_Data.dat");
        if ( !fil.exists() ) {
            fil.createNewFile();
            projectList = new ProjectList<Project>();
            projectList.save();
        } else {
            projectList = ProjectList.load();
        }
    }

    // practical methods

    public double getScreenH() {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        return screenSize.getHeight();
    }

    public double getScreenW() {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        return screenSize.getWidth();
    }

}

我遇到问题的值是 currentProjectIndex,它似乎正确保存了它的值,但是当 "createBug()" 方法是 运行 单击按钮时,它说它仍然-1(与初始化时一样)?

关于第一个问题,如您所见,我正在调用 window 来创建新项目或错误,然后 运行 调用 createProject() 或 createBug() 方法,通过点击一个按钮。但这就像我 运行 某些方法时未保存控制器 (selectedProjectIndex) 中的字段值?

总之我可以成功创建项目,将它们保存到文件中。但是在程序重新启动之前列表视图不会更新。

其次,我不能创建任何错误,因为出于某种原因,selectedProjectIndex 始终被视为 -1?

编辑:使用新建议更新了控制器 class。

当我尝试使用 showCreateProjectWindow() 或 showCreateBugWindow() 方法打开新 window 时的堆栈跟踪。

/C:/Users/Giuseppe/Dropbox/Java/BugTracker/bin/application/CreateProjectWindow.fxml

    at javafx.fxml.FXMLLoader.constructLoadException(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(Unknown Source)
    at javafx.fxml.FXMLLoader.load(Unknown Source)
    at application.MyController.showCreateProjectWindow(MyController.java:320)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.Trampoline.invoke(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(Unknown Source)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
    at javafx.event.Event.fireEvent(Unknown Source)
    at javafx.scene.control.MenuItem.fire(Unknown Source)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(Unknown Source)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren1(Unknown Source)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$$Lambda3/1964728071.handle(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(Unknown Source)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source)
    at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source)
    at javafx.event.Event.fireEvent(Unknown Source)
    at javafx.scene.Scene$MouseHandler.process(Unknown Source)
    at javafx.scene.Scene$MouseHandler.access00(Unknown Source)
    at javafx.scene.Scene.impl_processMouseEvent(Unknown Source)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent0(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$$Lambda2/1128114225.get(Unknown Source)
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(Unknown Source)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.handleMouseEvent(Unknown Source)
    at com.sun.glass.ui.View.notifyMouse(Unknown Source)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null5(Unknown Source)
    at com.sun.glass.ui.win.WinApplication$$Lambda/2117255219.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
    at application.MyController.updateListBuffers(MyController.java:399)
    at application.MyController.initialize(MyController.java:114)
    ... 65 more

你的问题几乎不可能回答,因为你的代码比重现问题所需的代码多得多,但你没有包含关键元素(例如 FXML 文件)。最好从头开始创建一个小的、简单的、完整的示例来演示问题,而不是发布您的控制器的全部内容 classes.

以下是一些可能适用于您遇到的问题的一般情况。

下面是调用 FXMLLoaderload(...) 方法时发生的基本过程:

  1. FXMLLoader 加载 FXML 文件。
  2. 如果根元素有 fx:controller 属性,并且没有 之前已设置控制器,加载程序将字符串转换为 Class。如果在加载程序上设置了 controllerFactory,那么将检索构造函数作为将 Class 传递给控制器​​工厂的 call(...) 方法的结果。否则,将通过指定的 class' 无参数构造函数实例化控制器。 (如果之前设置了控制器,则加载过程会异常终止。)
  3. 加载器解析FXML 文件,创建指定的对象,并将它们传递给彼此的各种set 方法,这些方法由FXML 结构决定。当创建了一个具有 fx:id 属性的对象,并且(通过任何方式)创建了一个控制器时,控制器中的一个 @FXML 注释字段与 fx:id 值(如果存在)被初始化为该对象。
  4. 如果控制器已创建,并且它有一个 initialize() 方法,则调用 initialize() 方法。
  5. 加载方法通过返回对与根元素对应的对象的引用来完成。

从编码的角度来看,这里有一些后果:

声明和初始化一个 @FXML-注释字段,例如:

@FXML
private Label myLabel = new Label();

总是多余的。如果fx:id设置正确,那么myLabel在加载过程中会被重新赋值,原来的初始化值会被丢弃。更糟糕的是,如果 fx:id 设置不正确,使用 myLabel 所做的任何操作都将应用于不属于场景图的标签,因此您将获得难以调试的意外结果(基本上,这些操作会成功,但在 UI 中没有可见的效果)。如果您不初始化该字段,那么如果 fx:id 设置不正确,它将立即失败并显示 NullPointerException,然后您可以轻松调试。

同样,在 initialize() 方法中重新初始化一个 @FXML 注释字段总是一场灾难:

@FXML
private Label myLabel ;

public void initialize() {
    myLabel = new Label(...);
    // ...
}

在这种情况下,myLabel 首先由 FXML 加载程序初始化,但是当 initialize() 方法被调用时,它被一个不属于场景图的不同标签替换。因此,对 myLabel 的操作将成功,但对 UI.

没有明显影响

其次,在 "standard" 控制器设置中,您不在 FXMLLoader 上调用 setControllersetControllerFactory,而是指定控制器 class 通过 fx:controller 属性,每个 FXMLLoader 创建对应控制器 class 的一个实例。从您的堆栈跟踪来看,ApplicationWindow.fxmlCreateProjectWindow.fxml 似乎都使用相同的控制器 class。这总是一个坏主意:共享控制器 class 的每个实例只有在对应于特定 FXML 文件定义的 FXML 元素时才会初始化 @FXML 注释字段。显然他们不会知道彼此的数据。您应该为每个 FXML 文件创建一个不同的控制器 class。如果他们需要共享数据,请使用 this question 中概述的技术。通常,您可以通过在一个控制器中创建和公开一个可观察对象 属性,并从另一个控制器观察它来管理它,如下例所示。

这是一个简单的例子,我猜你的例子有一些相同的结构。这可能有助于研究和理解这一点。这里的所有内容都放在一个名为 "application" 的包中(包括 FXML 文件)。

数据模型class:Person.java

package application;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Person {
    private final StringProperty firstName = new SimpleStringProperty();
    private final StringProperty lastName = new SimpleStringProperty();

    public Person(String firstName, String lastName) {
        setFirstName(firstName);
        setLastName(lastName);
    }

    public final StringProperty firstNameProperty() {
        return this.firstName;
    }

    public final String getFirstName() {
        return this.firstNameProperty().get();
    }

    public final void setFirstName(final String firstName) {
        this.firstNameProperty().set(firstName);
    }

    public final StringProperty lastNameProperty() {
        return this.lastName;
    }

    public final String getLastName() {
        return this.lastNameProperty().get();
    }

    public final void setLastName(final String lastName) {
        this.lastNameProperty().set(lastName);
    }

    @Override
    public String toString() {
        return getFirstName() +" "+getLastName();
    }
}

主 fxml 文件:PersonList.fxml:

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

<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>

<BorderPane xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="application.PersonListController">
    <center>
    <ListView fx:id="personList" />
</center>
    <bottom>
        <HBox spacing="5">
            <padding>
                <Insets top="10" bottom="10" left="10" right="10" />
            </padding>
            <Button text="New..." onAction="#newPerson" />
            <Button text="Edit..." onAction="#editPerson" fx:id="editButton" />
        </HBox>
    </bottom>
</BorderPane>

主控制器:PersonListController.java:

package application;

import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
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.Button;
import javafx.scene.control.ListView;
import javafx.stage.Stage;


public class PersonListController {
    @FXML
    private ListView<Person> personList ;
    @FXML
    private Button editButton ;

    public void initialize() {

        // list created so it fires updates if either the firstName or lastName change:
        ObservableList<Person> people = FXCollections.observableArrayList(person -> 
            new Observable[] {person.firstNameProperty(), person.lastNameProperty()});

        people.addAll(new Person("Jacob", "Smith"),
                new Person("Isabella", "Johnson"),
                new Person("Ethan", "Williams"),
                new Person("Emma", "Jones"),
                new Person("Michael", "Johnson"));
        personList.setItems(people);

        editButton.disableProperty().bind(
                Bindings.isNull(personList.getSelectionModel().selectedItemProperty()));
    }

    @FXML
    private void newPerson() throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("PersonEditor.fxml"));
        Parent root = loader.load();

        PersonEditorController controller = loader.getController();
        controller.personProperty().addListener((obs, oldPerson, newPerson) -> {
            if (newPerson != null) {
                personList.getItems().add(newPerson);
            }
        });

        showEditorWindow(root);
    }


    @FXML
    private void editPerson() throws Exception {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("PersonEditor.fxml"));
        Parent root = loader.load();

        PersonEditorController controller = loader.getController();
        controller.setPerson(personList.getSelectionModel().getSelectedItem());

        showEditorWindow(root);
    }

    private void showEditorWindow(Parent root) {
        Scene scene = new Scene(root, 350, 200);
        Stage stage = new Stage();
        stage.initOwner(personList.getScene().getWindow());
        stage.setScene(scene);
        stage.show();
    }

}

编辑器 fxml:PersonEditor.fxml:

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

<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>

<GridPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.PersonEditorController">
    <Label text="First Name:" GridPane.columnIndex="0"
        GridPane.rowIndex="0" />
    <Label text="Last Name:" GridPane.columnIndex="0"
        GridPane.rowIndex="1" />
    <TextField fx:id="firstNameTF" GridPane.columnIndex="1"
        GridPane.rowIndex="0" />
    <TextField fx:id="lastNameTF" GridPane.columnIndex="1"
        GridPane.rowIndex="1" />
    <HBox GridPane.columnIndex="0" GridPane.rowIndex="2"
        GridPane.columnSpan="2" spacing="5">
        <padding>
            <Insets top="5" bottom="5" left="5" right="5" />
        </padding>
        <Button text="OK" onAction="#submit" />
        <Button text="Cancel" onAction="#cancel" />
    </HBox>
</GridPane>

编辑器控制器:PersonEditorController.java:

package application;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;

public class PersonEditorController {
    @FXML
    private TextField firstNameTF ;
    @FXML
    private TextField lastNameTF ;

    private final ObjectProperty<Person> person = new SimpleObjectProperty<>();

    public ObjectProperty<Person> personProperty() {
        return person;
    }
    public final Person getPerson() {
        return personProperty().get();
    }
    public final void setPerson(Person person) {
        personProperty().set(person);
    }

    public void initialize() {
        personProperty().addListener((obs, oldPerson, newPerson) -> {
            if (newPerson != null) {
                firstNameTF.setText(newPerson.getFirstName());
                lastNameTF.setText(newPerson.getLastName());
            }
        });
    }

    @FXML
    private void submit() {
        if (person.get() == null) {
            person.set(new Person(firstNameTF.getText(), lastNameTF.getText()));
        } else {
            person.get().setFirstName(firstNameTF.getText());
            person.get().setLastName(lastNameTF.getText());
        }
        close();
    }

    @FXML
    private void cancel() {
        close();
    }

    private void close() {
        firstNameTF.getScene().getWindow().hide();
    }
}

申请:Main.java:

package application;

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.fxml.FXMLLoader;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("PersonList.fxml"));
            Scene scene = new Scene(root,400,400);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}