我试图通过从另一个应用程序实用程序类方法GlobalConfig.addSystemMessage()调用特定方法FXMLDocumentController.onAddSystemMessage()来更新我的fxml控制器中定义的javafx tableview。
这是我的主要Application类,我加载了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)
}
这也是我的实用程序类,其中包含一个向系统添加系统消息的方法。另外我想调用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();
}
}
以上实施依据: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$launchApplication1$163(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$176(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$174(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$175(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$null$149(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
希望我的目标很明确,上面的方法也不在范围之内。
答案 0 :(得分:2)
在途中只是给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
到控制器类的依赖关系(即除非你处于一个拥有环境的环境中,否则你无法重复使用它控制器)。换句话说,这里的紧耦合太多了。更好的方法是将控制器中的功能抽象为回调,您可以使用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 :(得分:1)
我做了什么来解决这个问题 - 我不认为这是最好的方法 -
1-在控制器中使用一个名为reload的新方法,该方法从数据库加载实体并将所有实体添加到表中。
2-在initialize()
上调用此方法3-将控制器传递给addSystemMessage()方法(或其他用于一致性的包装器方法)并调用reload @ its end(这就是为什么它可能不太好,因为它涉及对数据库的另一次调用,但它很有用f你想确保数据是同步的)
我没有尝试的其他方法:
1-让addSystemMessage()返回boolean以指示消息已正确添加到DB,如果为true,则使用systemMessages.getItems()addAll()
将新的SystemMessage对象从控制器内部添加到表中2-使用javafx绑定将UI表对象绑定到与数据库同步的其他对象可能有更好的方法(我没有搜索到这个,所以我不确定它的可行性)