从另一个类调用javafx fxml控制器方法来更新tableview

时间:2015-10-20 10:52:34

标签: javafx javafx-2 fxml fxmlloader

我试图通过从另一个应用程序实用程序类方法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();
        }
    }
  • GlobalConfig是一个实用程序类,它有一些从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$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)

希望我的目标很明确,上面的方法也不在范围之内。

2 个答案:

答案 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表对象绑定到与数据库同步的其他对象可能有更好的方法(我没有搜索到这个,所以我不确定它的可行性)