我试图通过从另一个应用程序实用程序类方法FXMLDocumentController.onAddSystemMessage()调用特定方法GlobalConfig.addSystemMessage()来更新fxml控制器中定义的javafx。
下面是我的主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(...)方法使用新项更新表视图:
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)希望我的目标是明确的,上面的方法没有超出范围。
发布于 2015-10-20 12:01:08
在此过程中,只需向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来表示回调。
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运行在后台线程中,则需要更新上的UI,您可以使用
config.setMessageProcessor((SystemMessage msg) ->
Platform.runLater(() -> onAddSystemMessage(msg));发布于 2015-10-20 12:18:25
我为解决这个问题所做的事情,-Which,我并不认为是最好的方法-:
1-在控制器中使用一个名为reload的新方法,该方法从数据库加载实体并将所有实体添加到表中。
2-在初始化()时调用此方法
3-将控制器传递给addSystemMessage()方法(或其他一致性包装方法)并调用reload @它的结束(这就是为什么它可能不是很好,因为它涉及到对数据库的另一次调用,但如果您希望确保数据是同步的,这是很有用的)
其他我没有尝试过的方法:
让addSystemMessage()返回布尔值以指示消息正确地添加到DB中,如果为真,则使用systemMessages.getItems()addAll()将新的SystemMessage对象从控制器内部添加到表中
2-可能有更好的方法使用javafx绑定将UI表对象绑定到与数据库同步的其他对象(我没有搜索它,所以我不确定它是否可行)。
https://stackoverflow.com/questions/33234712
复制相似问题