从另一个 class 调用 javafx fxml 控制器方法来更新 tableview
Call a javafx fxml controller method from another class to update a tableview
我正在尝试通过从另一个应用程序实用程序 class 方法 GlobalConfig.addSystemMessage() 调用特定方法 FXMLDocumentController.onAddSystemMessage() 来更新在我的 fxml 控制器中定义的 javafx tableview。
这是我的主要应用程序 class,我在其中加载了 fxml:
public class Main extends Application {
...
public static void main(String[] args) throws IOException {
Application.launch(args);
}
...
@Override
public void start(Stage primaryStage) throws IOException {
AnchorPane page = (AnchorPane) FXMLLoader.load(Main.class.getResource("FXMLDocument.fxml"));
Scene scene = new Scene(page, initWidth, initHeight);
primaryStage.setScene(scene);
currentPane(scene, page);
primaryStage.show();
}
下面是 FXMLDocumentController 的一些部分:
public class FXMLDocumentController implements Initializable {
...
@FXML
private TableView<SystemMessage> systemMessages;
@FXML
private TableColumn<SystemMessage, DateTime> dateSysReceived;
@FXML
private TableColumn<SystemMessage, String> messageText;
@FXML
private TableColumn<SystemMessage, String> messageType;
...
private ObservableList<SystemMessage> messagesData;
...
private GlobalConfig globalConfig;
...
@Override
@FXML
public void initialize(URL url, ResourceBundle rb) {
config = new GlobalConfig();
...
messagesData = FXCollections.observableArrayList();
messagesData = getAllMessages();
systemMessages.getItems().setAll(messagesData);
dateSysReceived.setCellValueFactory(new PropertyValueFactory<>("dateSysReceived"));
messageText.setCellValueFactory(new PropertyValueFactory<>("messageText"));
messageType.setCellValueFactory(new PropertyValueFactory<>("messageType"));
...
}
...
private ObservableList<SystemMessage> getAllMessages() {
ObservableList<SystemMessage> data = FXCollections.observableArrayList();
data = FXCollections.observableArrayList();
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
List<SystemMessage> allMessages = new ArrayList<>();
allMessages = msgDAO.listSystemMessage();
for(SystemMessage msg: allMessages) {
data.add(msg);
}
return data;
}
... // and here is the method that i would like to call to add new record to tableview
public void onAddSystemMessage(SystemMessage systemMessage) {
log.info("Add System Message called!");
// to DO ... add item to tableview
//this method should be called when inserting new systemMessage (DAO)
}
这也是我的实用程序 class,其中包含一种将系统消息添加到数据库的方法。另外我想调用 FXMLDocumentController.onAddSystemMessage(...) 方法用新项目更新 tableview:
public final class GlobalConfig {
...
//constructor
public GlobalConfig () {
...
}
public void addSystemMessage(String messageText, String messageType) {
SystemMessage msg = new SystemMessage();
DateTime cur = DateTime.now();
try {
msg.setDateSysReceived(cur);
msg.setMessageText(messageText);
msg.setMessageType(messageType);
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
msgDAO.addSystemMessage(msg);
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocumentController.fxml"));
FXMLDocumentController controller = (FXMLDocumentController)loader.getController();
//just call my Controller method and pass msg
controller.onAddSystemMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
- GlobalConfig 是一个实用程序 class,它有一些方法可以从 db 中获取参数,还可以执行一些工作,例如向 db 表添加一些新值。它是从我的应用程序的几个部分调用的,我想获取当前的 FXMLDocumentController 对象并调用它的方法 onAddSystemMessage() 来更新 UI.
以上实现是根据:Accessing FXML controller class 但是我得到的是:
java.lang.NullPointerException
at com.npap.utils.GlobalConfig.addSystemMessage(GlobalConfig.java:85)
at com.npap.dicomrouter.FXMLDocumentController.startDcmrcvService(FXMLDocumentController.java:928)
at com.npap.dicomrouter.FXMLDocumentController.initialize(FXMLDocumentController.java:814)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
at com.npap.dicomrouter.Main.start(Main.java:141)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication13(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait6(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null4(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater5(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null9(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
希望我的 objective 是清楚的,并且上述方法没有超出范围。
方法是简单地给 GlobalConfig
一个控制器的引用:
public final class GlobalConfig {
private FXMLDocumentController controller ;
public void setController(FXMLDocumentController controller) {
this.controller = controller ;
}
...
public void addSystemMessage(String messageText, String messageType) {
SystemMessage msg = new SystemMessage();
DateTime cur = DateTime.now();
try {
msg.setDateSysReceived(cur);
msg.setMessageText(messageText);
msg.setMessageType(messageType);
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
msgDAO.addSystemMessage(msg);
if (controller != null) {
controller.onAddSystemMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后在创建 GlobalConfig
:
时将引用传递给控制器
public void initialize(URL url, ResourceBundle rb) {
config = new GlobalConfig();
config.setController(this));
...
messagesData = FXCollections.observableArrayList();
messagesData = getAllMessages();
systemMessages.getItems().setAll(messagesData);
dateSysReceived.setCellValueFactory(new PropertyValueFactory<>("dateSysReceived"));
messageText.setCellValueFactory(new PropertyValueFactory<>("messageText"));
messageType.setCellValueFactory(new PropertyValueFactory<>("messageType"));
...
}
我不太喜欢这个解决方案,因为它引入了从 GlobalConfig
到控制器 class 的依赖关系(即你不能重用它,除非你在一个环境中你有一个控制器)。换句话说,这里的耦合太紧了。更好的方法是将控制器的功能抽象为回调,您可以用 Consumer<SystemMesage>
:
表示
public final class GlobalConfig {
private Consumer<SystemMessage> messageProcessor ;
public void setMessageProcessor(Consumer<SystemMessage> messageProcessor) {
this.messageProcessor = messageProcessor ;
}
...
public void addSystemMessage(String messageText, String messageType) {
SystemMessage msg = new SystemMessage();
DateTime cur = DateTime.now();
try {
msg.setDateSysReceived(cur);
msg.setMessageText(messageText);
msg.setMessageType(messageType);
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
msgDAO.addSystemMessage(msg);
if (messageProcessor != null) {
messageProcessor.accept(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后你可以做
public void initialize(URL url, ResourceBundle rb) {
config = new GlobalConfig();
config.setMessageProcessor(this::onAddSystemMessage);
...
messagesData = FXCollections.observableArrayList();
messagesData = getAllMessages();
systemMessages.getItems().setAll(messagesData);
dateSysReceived.setCellValueFactory(new PropertyValueFactory<>("dateSysReceived"));
messageText.setCellValueFactory(new PropertyValueFactory<>("messageText"));
messageType.setCellValueFactory(new PropertyValueFactory<>("messageType"));
...
}
如果您的 GlobalConfig
在后台线程中 运行,您将需要更新 FX 应用程序线程上的 UI,您可以使用
config.setMessageProcessor((SystemMessage msg) ->
Platform.runLater(() -> onAddSystemMessage(msg));
我为解决这个问题所做的工作 - 我不认为这是最好的方法 - :
1- 在控制器中使用一个名为 reload 的新方法,它从数据库中加载实体并将其全部添加到 table。
2- 在 initialize() 上调用此方法
3- 将控制器传递给 addSystemMessage() 方法(或其他用于连贯性的包装方法)并在其末尾调用重新加载(这就是为什么它可能不太好,因为它涉及对数据库的另一个调用,但它是如果您想确保数据同步,这很有用)
其他我没有尝试过的方法:
1- 让 addSystemMessage() return 布尔值指示消息已正确添加到数据库,如果为真,则使用 [= 从控制器内部将新的 SystemMessage 对象添加到 table 24=]()添加全部()
2- 可能有更好的方法使用 javafx 绑定将您的 UI table 对象与与数据库同步的其他对象绑定(我没有搜索,所以我不确定关于其可行性)
我正在尝试通过从另一个应用程序实用程序 class 方法 GlobalConfig.addSystemMessage() 调用特定方法 FXMLDocumentController.onAddSystemMessage() 来更新在我的 fxml 控制器中定义的 javafx tableview。
这是我的主要应用程序 class,我在其中加载了 fxml:
public class Main extends Application {
...
public static void main(String[] args) throws IOException {
Application.launch(args);
}
...
@Override
public void start(Stage primaryStage) throws IOException {
AnchorPane page = (AnchorPane) FXMLLoader.load(Main.class.getResource("FXMLDocument.fxml"));
Scene scene = new Scene(page, initWidth, initHeight);
primaryStage.setScene(scene);
currentPane(scene, page);
primaryStage.show();
}
下面是 FXMLDocumentController 的一些部分:
public class FXMLDocumentController implements Initializable {
...
@FXML
private TableView<SystemMessage> systemMessages;
@FXML
private TableColumn<SystemMessage, DateTime> dateSysReceived;
@FXML
private TableColumn<SystemMessage, String> messageText;
@FXML
private TableColumn<SystemMessage, String> messageType;
...
private ObservableList<SystemMessage> messagesData;
...
private GlobalConfig globalConfig;
...
@Override
@FXML
public void initialize(URL url, ResourceBundle rb) {
config = new GlobalConfig();
...
messagesData = FXCollections.observableArrayList();
messagesData = getAllMessages();
systemMessages.getItems().setAll(messagesData);
dateSysReceived.setCellValueFactory(new PropertyValueFactory<>("dateSysReceived"));
messageText.setCellValueFactory(new PropertyValueFactory<>("messageText"));
messageType.setCellValueFactory(new PropertyValueFactory<>("messageType"));
...
}
...
private ObservableList<SystemMessage> getAllMessages() {
ObservableList<SystemMessage> data = FXCollections.observableArrayList();
data = FXCollections.observableArrayList();
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
List<SystemMessage> allMessages = new ArrayList<>();
allMessages = msgDAO.listSystemMessage();
for(SystemMessage msg: allMessages) {
data.add(msg);
}
return data;
}
... // and here is the method that i would like to call to add new record to tableview
public void onAddSystemMessage(SystemMessage systemMessage) {
log.info("Add System Message called!");
// to DO ... add item to tableview
//this method should be called when inserting new systemMessage (DAO)
}
这也是我的实用程序 class,其中包含一种将系统消息添加到数据库的方法。另外我想调用 FXMLDocumentController.onAddSystemMessage(...) 方法用新项目更新 tableview:
public final class GlobalConfig {
...
//constructor
public GlobalConfig () {
...
}
public void addSystemMessage(String messageText, String messageType) {
SystemMessage msg = new SystemMessage();
DateTime cur = DateTime.now();
try {
msg.setDateSysReceived(cur);
msg.setMessageText(messageText);
msg.setMessageType(messageType);
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
msgDAO.addSystemMessage(msg);
FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocumentController.fxml"));
FXMLDocumentController controller = (FXMLDocumentController)loader.getController();
//just call my Controller method and pass msg
controller.onAddSystemMessage(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
- GlobalConfig 是一个实用程序 class,它有一些方法可以从 db 中获取参数,还可以执行一些工作,例如向 db 表添加一些新值。它是从我的应用程序的几个部分调用的,我想获取当前的 FXMLDocumentController 对象并调用它的方法 onAddSystemMessage() 来更新 UI.
以上实现是根据:Accessing FXML controller class 但是我得到的是:
java.lang.NullPointerException
at com.npap.utils.GlobalConfig.addSystemMessage(GlobalConfig.java:85)
at com.npap.dicomrouter.FXMLDocumentController.startDcmrcvService(FXMLDocumentController.java:928)
at com.npap.dicomrouter.FXMLDocumentController.initialize(FXMLDocumentController.java:814)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
at com.npap.dicomrouter.Main.start(Main.java:141)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication13(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait6(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null4(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater5(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null9(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
希望我的 objective 是清楚的,并且上述方法没有超出范围。
方法是简单地给 GlobalConfig
一个控制器的引用:
public final class GlobalConfig {
private FXMLDocumentController controller ;
public void setController(FXMLDocumentController controller) {
this.controller = controller ;
}
...
public void addSystemMessage(String messageText, String messageType) {
SystemMessage msg = new SystemMessage();
DateTime cur = DateTime.now();
try {
msg.setDateSysReceived(cur);
msg.setMessageText(messageText);
msg.setMessageType(messageType);
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
msgDAO.addSystemMessage(msg);
if (controller != null) {
controller.onAddSystemMessage(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后在创建 GlobalConfig
:
public void initialize(URL url, ResourceBundle rb) {
config = new GlobalConfig();
config.setController(this));
...
messagesData = FXCollections.observableArrayList();
messagesData = getAllMessages();
systemMessages.getItems().setAll(messagesData);
dateSysReceived.setCellValueFactory(new PropertyValueFactory<>("dateSysReceived"));
messageText.setCellValueFactory(new PropertyValueFactory<>("messageText"));
messageType.setCellValueFactory(new PropertyValueFactory<>("messageType"));
...
}
我不太喜欢这个解决方案,因为它引入了从 GlobalConfig
到控制器 class 的依赖关系(即你不能重用它,除非你在一个环境中你有一个控制器)。换句话说,这里的耦合太紧了。更好的方法是将控制器的功能抽象为回调,您可以用 Consumer<SystemMesage>
:
public final class GlobalConfig {
private Consumer<SystemMessage> messageProcessor ;
public void setMessageProcessor(Consumer<SystemMessage> messageProcessor) {
this.messageProcessor = messageProcessor ;
}
...
public void addSystemMessage(String messageText, String messageType) {
SystemMessage msg = new SystemMessage();
DateTime cur = DateTime.now();
try {
msg.setDateSysReceived(cur);
msg.setMessageText(messageText);
msg.setMessageType(messageType);
SystemMessageDAO msgDAO = new SystemMessageDAOImpl();
msgDAO.addSystemMessage(msg);
if (messageProcessor != null) {
messageProcessor.accept(msg);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
然后你可以做
public void initialize(URL url, ResourceBundle rb) {
config = new GlobalConfig();
config.setMessageProcessor(this::onAddSystemMessage);
...
messagesData = FXCollections.observableArrayList();
messagesData = getAllMessages();
systemMessages.getItems().setAll(messagesData);
dateSysReceived.setCellValueFactory(new PropertyValueFactory<>("dateSysReceived"));
messageText.setCellValueFactory(new PropertyValueFactory<>("messageText"));
messageType.setCellValueFactory(new PropertyValueFactory<>("messageType"));
...
}
如果您的 GlobalConfig
在后台线程中 运行,您将需要更新 FX 应用程序线程上的 UI,您可以使用
config.setMessageProcessor((SystemMessage msg) ->
Platform.runLater(() -> onAddSystemMessage(msg));
我为解决这个问题所做的工作 - 我不认为这是最好的方法 - :
1- 在控制器中使用一个名为 reload 的新方法,它从数据库中加载实体并将其全部添加到 table。
2- 在 initialize() 上调用此方法
3- 将控制器传递给 addSystemMessage() 方法(或其他用于连贯性的包装方法)并在其末尾调用重新加载(这就是为什么它可能不太好,因为它涉及对数据库的另一个调用,但它是如果您想确保数据同步,这很有用)
其他我没有尝试过的方法:
1- 让 addSystemMessage() return 布尔值指示消息已正确添加到数据库,如果为真,则使用 [= 从控制器内部将新的 SystemMessage 对象添加到 table 24=]()添加全部()
2- 可能有更好的方法使用 javafx 绑定将您的 UI table 对象与与数据库同步的其他对象绑定(我没有搜索,所以我不确定关于其可行性)