无法强制JavaFX TableView从另一个窗口/控制器刷新

时间:2019-03-30 11:10:58

标签: javafx tableview

我有两个窗口和两个控制器。

在第一个窗口中是一个按钮,用于打开第二个窗口和TableView。第二个窗口是一种形式,我可以在其中插入类的属性。插入后,我单击“保存”,然后将新创建的对象添加到静态列表中。这应该显示在TableView中。当我仅使用一个窗口和一个控制器时,一切工作正常,但如两个窗口中所述拆分功能,则不会刷新TableView。

这是我的最小示例:

主控制器

public class Controller {

    @FXML
    public TableView tableView;

    @FXML
    private Button openWindow;


    // called by the FXML loader after the labels declared above are injected:
    public void initialize() {

        TableColumn mailColumn = new TableColumn("E-Mail");
        tableView.getColumns().addAll(mailColumn);
        mailColumn.setCellValueFactory(new PropertyValueFactory<Person,String>("mail"));


        openWindow.setOnAction((event) -> {
            new PersonInputForm(event);
        });


    }

    // ######   Receiver TableView Action Handling #######
    public void updateReceiverList(){
        final ObservableList<Person> data = FXCollections.observableArrayList(Memory.receiverList);
        tableView.getItems().clear();
        tableView.getItems().addAll(data);
    }

}

辅助控制器

public class PersonInputFormController {


    @FXML
    private TextField mail;

    @FXML
    private AnchorPane personInputFormAnchorPane;

    private Controller mainController ;
    Stage stage;

    public void setStage() {
        stage = (Stage) personInputFormAnchorPane.getScene().getWindow();
    }

    public void setMainController(Controller mainController){
        this.mainController = mainController;
    }

    public void save(){
        Memory.saveReceiver(mail);
        mainController.updateReceiverList();
        stage.close();
    }


}

主窗口

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{

        FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);
        primaryStage.show();
    }

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

次要窗口

public class PersonInputForm {

    public PersonInputForm(ActionEvent event) {

        Stage stage = new Stage();

        FXMLLoader mainFxmlLoader = new FXMLLoader(getClass().getResource("../sample.fxml"));
        try {
            mainFxmlLoader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Controller mainController = mainFxmlLoader.getController();


        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("PersonInputForm.fxml"));
        Parent root = null;
        try {
            root = (Parent)fxmlLoader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }

        stage.setScene(new Scene(root));
        stage.initOwner(
                ((Node) (event.getSource())).getScene().getWindow() );

        PersonInputFormController controller = fxmlLoader.<PersonInputFormController>getController();
        controller.setStage();
        controller.setMainController(mainController);

        stage.show();

    }
}

记忆力

public class Memory {

    public static sample.Person sender = new sample.Person();
    public static ArrayList<sample.Person> receiverList = new ArrayList();

    static public void saveReceiver(TextField mail){
        Person receiver = new Person();
        receiver.setMail(mail.getText());
        receiverList.add(receiver);
    }

}

对象

public class Person {

    private SimpleStringProperty mail = new SimpleStringProperty();

    public String getMail() {
        return mail.get();
    }

    public SimpleStringProperty mailProperty() {
        return mail;
    }

    public void setMail(String mail) {
        this.mail.set(mail);
    }

}

我发现了以下类似的主题:

How to set items of a TableView in a different controller class in JavaFX?

Javafx Update a tableview from another FXML

但是,我仍然不知道如何解决我的问题。

谢谢!

1 个答案:

答案 0 :(得分:0)

在您的PersonInputForm类的构造函数中,创建一个FXMLLoader来加载sample.fxml。然后,将此负载(一个Controller实例)所产生的控制器提供给以后生成的PersonInputFormController实例。与此有关的问题是,调用Controller的{​​{1}}实例与您赋予new PersonInputForm(event)不是同一实例。请记住,每次调用PersonInputFormController时,都会创建一个连接到 new 控制器实例的 new 场景图。

当您从FXMLLoader.load的一个实例中调用new PersonInputForm(event)时,您需要在Controller的这个实例上调用Controller,最简单的解决方法是通过也从updateReceiverList()this。然后删除负责加载PersonInputForm的代码。

sample.fxml

注意:在构造函数中具有所有这些行为是错误的。

尽管以上所述可以解决您的问题(我相信),但必须清除并替换public PersonInputForm(ActionEvent event, Controller mainController) { // code... controller.setMainController(mainController); } 的项目并不是理想的方法。看看TableView

updateReceiverList()

该代码将一个收藏复制到另一个收藏两次。当您具有大量元素时,这可能会变得非常昂贵。如果您有一个不同的控制器可以观察并做出反应的可观察模型,那就更好了。

例如,您正在使用public void updateReceiverList(){ // Copies Memory.receiverList into new ObservableList final ObservableList<Person> data = FXCollections.observableArrayList(Memory.receiverList); tableView.getItems().clear(); // Copies data into the TableView's items list tableView.getItems().addAll(data); } 存储/共享ArrayList,而您可能会使用Person。然后,您可以使用ObservableListtableView.setItems(Memory.receiverList)会自动看到PersonInputFormControllerreceiverList所做的任何更新。由于您似乎没有使用多线程,因此此设置不会造成任何问题。当或开始使用后台线程时,请记住,绝对不要从后台线程直接或间接更新UI。

但是,使用全局状态(即公共静态变量)也不理想。我建议阅读不同的应用程序体系结构,例如:Model-View-Controller(MVC),Model-View-Presenter(MVP),Model-View-ViewModel(MVVP)等。然后尝试将其中一种应用于您的应用程序作为适当的。


一些链接:


我想指出关于您的代码的另一件事。您正在使用很多原始类型。 Don't use raw types。例如,您应该具有:

TableView

并且:

@FXML private TableView<Person> tableView;

并且:

TableColumn<Person, String> mailColumn = new TableColumn<>("E-Mail");
// which lets you use the diamond operator here
mailColumn.setCellValueFactory(new PropertyValueFactory<>("mail"));