我有两个窗口和两个控制器。
在第一个窗口中是一个按钮,它打开第二个窗口和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
但是,我仍然不明白我的问题是如何解决的。
提前致谢!
在你的PersonInputForm
类的构造函数中,你创建一个FXMLLoader
来加载sample.fxml
。然后,您将由此负载生成的控制器 - 一个Controller
实例 - 提供给您稍后生成的PersonInputFormController
实例。问题是调用Controller
的new 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
存储/分享你的Person
s,你可以使用ObservableList
。然后你可以使用tableView.setItems(Memory.receiverList)
,你的PersonInputFormController
对receiverList
的更新将由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<>();