使用JavaFX更新从ActionEvent线程调用的方法中的UI

时间:2018-01-23 09:17:07

标签: javafx methods runnable

我想在用户登录应用程序时更改阶段。 我在Action中创建了一个线程,在其中我使用Platform.runLater来更新舞台并显示新的舞台。这是在被调用的方法中完成的。

所以我有以下代码:

的LoginController

private Stage primaryStage

@FXML
void btnLoginAction(ActionEvent event) throws ClassNotFoundException {
    Runnable loginRunnable = new Runnable() {
        public void run() {
            ....
        if (user exists and password ok){
           loadMainwindow();
        }else{
           show alert
        }
    };
    Thread loginThread = new Thread(loginRunnable);
    loginThread.start();
}

private void loadMainWindow() throws IOException {

    dummyStage =  (Stage) (btnLogin.getScene()).getWindow();

    //I get the root borderpain from the Main class
    BorderPane root = Main.getRoot();

    //I load the anchorpanes i will use in the new stage
    AnchorPane menuPane = 
    FXMLLoader.load(getClass().getResource("/views/Menu.fxml"));
    AnchorPane centerPane = 
    FXMLLoader.load(getClass().getResource("/views/Home.fxml"));

    //I set the anchorpanes to the root
    root.setLeft(menuPane);
    root.setCenter(centerPane);

    Platform.runLater(new Runnable() {
        public void run() {
            primaryStage.show();
        }
    });
}

我遇到以下错误:

Exception in thread "Thread-3" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-3
at javafx.graphics/com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:493)
at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
at javafx.graphics/javafx.scene.layout.BorderPane$BorderPositionProperty.invalidated(BorderPane.java:692)
at javafx.base/javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
at javafx.base/javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
at javafx.graphics/javafx.scene.layout.BorderPane.setLeft(BorderPane.java:325)
at com.sener.dbgui.controller.LoginController.loadMainWindow(LoginController.java:90)
at com.sener.dbgui.controller.LoginController.access$4(LoginController.java:81)
at com.sener.dbgui.controller.LoginController$1.run(LoginController.java:63)
at java.base/java.lang.Thread.run(Thread.java:844)

第81行是“root.setLeft(menuPane)”行。

所以我猜问题是在修改根边框时必须运行JAVAFX线程。这是,我必须在Platform.runLater方法中包含“root.set ...”语句。

尽管如此,这意味着在控制器类中将root,menuPane和centerPane的多个变量设置为private,以便Platform.runLater进程可以访问它们,并且所有FXMLLoader,getwindow()和getRoot()方法都可以从Platform.runLater。

那么,将这个变量设置为private还是只调用Platform.runLater中的方法会更好吗?

选项1.在Platform.runLater内部调用方法

@FXML
void btnLoginAction(ActionEvent event) throws ClassNotFoundException {
    Runnable loginRunnable = new Runnable() {
        public void run() {
            ....
        if (user exists and password ok){
            Platform.runLater(new Runnable() {
                public void run() { 
                  loadMainwindow();
                }
            });
        }else{
           show alert
        }
    };
    Thread loginThread = new Thread(loginRunnable);
    loginThread.start();
}

如果从Platform.runLater解耦FXMLLoader,getWindow()和getRoot方法,该方法的代码将如下所示(我首先为AnchorPanes创建私有变量“menuPane”和“centerPane”,BorderPane“root”就像使用“primaryStage”变量):

选项2. CALL方法和DECOUPLE FMLXLOADERS,GETROOT()和GETWINDOW()方法来自Platform.runLater()

private AnchorPane menuPane, centerPane;
private Stage dummyStage;
private BorderPane root;

@FXML
void btnLoginAction(ActionEvent event) throws ClassNotFoundException {
Runnable loginRunnable = new Runnable() {
    public void run() {
        ....
    if (user exists and password ok){
       loadMainwindow();
    }else{
       show alert
    }
};
Thread loginThread = new Thread(loginRunnable);
loginThread.start();
}


private void loadMainWindow() throws IOException {

    root = Main.getRoot();

    primaryStage =  (Stage) (btnLogin.getScene()).getWindow();

    menuPane = FXMLLoader.load(getClass().getResource("/views/Menu.fxml"));
    centerPane = XMLLoader.load(getClass().getResource("/views/Home.fxml"));

    Platform.runLater(new Runnable() {
        public void run() {

            root.setLeft(menuPane);
            root.setCenter(centerPane);

            primaryStage.toFront();
            primaryStage.show();
        }
    });
}

我想知道哪个选项是正确的。或许这些都是错误的,还有另一个解决方案。

0 个答案:

没有答案