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

问题描述 投票:1回答:1

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

在第一个窗口中是一个按钮,它打开第二个窗口和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

但是,我仍然不明白我的问题是如何解决的。

提前致谢!

javafx tableview
1个回答
0
投票

在你的PersonInputForm类的构造函数中,你创建一个FXMLLoader来加载sample.fxml。然后,您将由此负载生成的控制器 - 一个Controller实例 - 提供给您稍后生成的PersonInputFormController实例。问题是调用Controllernew PersonInputForm(event)实例与你给PersonInputFormController的实例不同。请记住,每次调用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存储/分享你的Persons,你可以使用ObservableList。然后你可以使用tableView.setItems(Memory.receiverList),你的PersonInputFormControllerreceiverList的更新将由TableView自动查看。由于您似乎没有使用多线程,因此此设置不应导致任何问题。何时或者如果您开始使用后台线程,请记住,您必须永远不要直接或间接地从后台线程更新UI。

但是,使用全局状态(即公共静态变量)也不理想。我建议阅读不同的应用程序体系结构,例如:模型 - 视图 - 控制器(MVC),模型 - 视图 - 展示器(MVP),模型 - 视图 - 视图模型(MVVP)等。然后尝试将其中一个应用于您的应用程序作为适当的。


一些链接:


我想再指出一下你的代码。你使用了很多原始类型。 Don't use raw types。例如,您应该:

@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"));

和:

// You're missing the <> on the right hand side
public static ArrayList<Person> receiverList = new ArrayList<>();
© www.soinside.com 2019 - 2024. All rights reserved.