JavaFX多窗口场景下TableView无法刷新问题求助
问题分析与解决方案
你的核心问题出在子窗口获取主控制器的方式错误,导致刷新操作没有作用在实际显示的TableView上,另外还有数据结构可以优化来简化刷新逻辑。下面一步步给你解决:
1. 关键错误:获取了错误的主控制器实例
在PersonInputForm的构造方法里,你加载了sample.fxml并新建了一个Controller对象:
FXMLLoader mainFxmlLoader = new FXMLLoader(getClass().getResource("../sample.fxml")); // ... Controller mainController = mainFxmlLoader.getController();
这完全是一个新的控制器实例,和主窗口正在显示的那个没有任何关系!你调用它的updateReceiverList(),操作的是这个新控制器里的TableView(根本没显示在界面上),所以原窗口的TableView自然不会刷新。
正确的做法是:在主控制器打开子窗口时,把自身的实例传递给子窗口。
2. 优化数据结构,让刷新更简单
现在你用ArrayList存储数据,每次刷新都要手动创建ObservableList再重新添加,其实可以直接把Memory里的receiverList改成ObservableList,这样TableView可以直接绑定这个列表,数据变化时自动刷新,不用手动调用clear和addAll。
修改后的完整代码
主控制器(Controller.java)
public class Controller { @FXML public TableView<Person> tableView; // 加上泛型,避免未检查警告 @FXML private Button openWindow; public void initialize() { TableColumn<Person, String> mailColumn = new TableColumn<>("E-Mail"); // 加上泛型 tableView.getColumns().addAll(mailColumn); mailColumn.setCellValueFactory(new PropertyValueFactory<>("mail")); // 直接绑定Memory里的ObservableList,后续数据变化自动刷新 tableView.setItems(Memory.receiverList); openWindow.setOnAction((event) -> { // 打开子窗口时,把当前主控制器实例传过去 new PersonInputForm(event, this); }); } // 这个方法可以删掉了,因为TableView已经绑定了ObservableList // public void updateReceiverList(){...} }
子窗口类(PersonInputForm.java)
修改构造方法,接收主控制器实例:
public class PersonInputForm { // 接收主控制器作为参数 public PersonInputForm(ActionEvent event, Controller mainController) { Stage stage = new Stage(); FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("PersonInputForm.fxml")); Parent root = null; try { root = fxmlLoader.load(); } catch (IOException e) { e.printStackTrace(); } stage.setScene(new Scene(root)); stage.initOwner( ((Node) (event.getSource())).getScene().getWindow() ); PersonInputFormController controller = fxmlLoader.getController(); controller.setStage(); controller.setMainController(mainController); stage.show(); } }
子控制器(PersonInputFormController.java)
现在保存后直接操作ObservableList,不用再调用主控制器的刷新方法:
public class PersonInputFormController { @FXML private TextField mail; @FXML private AnchorPane personInputFormAnchorPane; private Stage stage; public void setStage() { stage = (Stage) personInputFormAnchorPane.getScene().getWindow(); } // 现在不需要主控制器了,因为数据直接存在ObservableList里 // private Controller mainController ; // public void setMainController(Controller mainController){...} public void save(){ Memory.saveReceiver(mail); // 不用再调用主控制器的方法,TableView会自动刷新 stage.close(); } }
Memory类
把receiverList改成ObservableList:
public class Memory { public static sample.Person sender = new sample.Person(); // 直接用ObservableList,TableView可以绑定它 public static ObservableList<Person> receiverList = FXCollections.observableArrayList(); static public void saveReceiver(TextField mail){ Person receiver = new Person(); receiver.setMail(mail.getText()); receiverList.add(receiver); } }
为什么这样修改有效?
- 传递正确的主控制器实例:现在子窗口从主控制器那里直接拿到正在显示的Controller对象,而不是新建一个。不过我们后来优化了数据结构,甚至不需要主控制器了,因为ObservableList会自动通知TableView更新。
- ObservableList的自动更新:当你往
Memory.receiverList里添加新的Person对象时,因为它是ObservableList,已经和TableView绑定,所以TableView会自动刷新显示新数据,完全不需要手动调用clear和addAll。
这样修改后,你拆分窗口后的功能就和单窗口时一样正常啦!
内容的提问来源于stack exchange,提问作者Mr. T
相关产品推荐
相关产品推荐

