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



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

在第一个窗口中是一个按钮,用于打开第二个窗口和 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);
}
}

我发现了以下听起来相似的话题:

如何在 JavaFX 中的不同控制器类中设置表视图的项目?

Javafx 从另一个 FXML 更新表视图

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

提前感谢!

PersonInputForm类的构造函数中,您创建一个FXMLLoader来加载sample.fxml。然后,您将此负载产生的控制器(Controller实例)提供给稍后生成的PersonInputFormController实例。这样做的问题是调用new PersonInputForm(event)Controller实例与您提供给PersonInputFormController实例不同。请记住,每次调用FXMLLoader.load时,都会创建一个连接到控制器实例的新场景图。

当您从Controller实例中调用new PersonInputForm(event)时,您需要调用updateReceiverList()的正是此Controller实例,最简单的解决方法是将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,而您可以使用ObservableList。然后,您可以使用tableView.setItems(Memory.receiverList)PersonInputFormControllerreceiverList所做的任何更新都将被TableView自动看到。由于您似乎没有使用多线程,因此此设置应该不会引起任何问题。当您开始使用后台线程时,请记住,切勿从后台线程直接或间接更新 UI。

但是,使用全局状态(即公共静态变量)也不理想。我建议阅读不同的应用程序架构,例如:模型-视图-控制器(MVC),模型-视图-呈现器(MVP),模型-视图-视图模型(MVVP)等。然后尝试根据需要将其中一个应用于您的应用程序。


一些链接:

  • 使用 JavaFx 应用 MVC
  • 传递参数 JavaFX FXML

我想指出关于您的代码的另一件事。您正在使用大量原始类型。不要使用原始类型。例如,您应该拥有:

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

最新更新