从JavaFX平台runlater返回结果(Return result from javafx pl

2019-06-21 21:10发布

我工作在JavaFX应用程序,在我的情况是显示在JavaFX中创建一个密码提示这需要密码有两个选项, OKCancel 。 我已经回到由用户输入的密码。

我的班级显示密码对话框的是 -

public static String showPasswordDialog(String title, String message, Stage parentStage, double w, double h) {
    try {
        Stage stage = new Stage();
        PasswordDialogController controller = (PasswordDialogController) Utility.replaceScene("Password.fxml", stage);
        passwordDialogController.init(stage, message, "/images/password.png");
        if (parentStage != null) {
            stage.initOwner(parentStage);
        }
        stage.initModality(Modality.WINDOW_MODAL);
        stage.initStyle(StageStyle.UTILITY);
        stage.setResizable(false);
        stage.setWidth(w);
        stage.setHeight(h);                
        stage.showAndWait();
        return controller.getPassword(); 
    } catch (Exception ex) {
         return null;
    }

我的代码在哪里显示密码提示低于,居然会被显示在其他UI此提示,所以我需要附寄这里面Platform.runlater()否则它会引发Not on FX application thread 。 我需要显示此提示输入密码,直到我得到正确的。 我怎样才能得到密码的值,如果我围封显示里面runlater密码。

有没有其他更好的办法?

final String sPassword = null;

          do {
            Platform.runLater(new Runnable() {

                @Override
                public void run() {
                     sPassword = JavaFXDialog.showPasswordDialog(sTaskName + "Password", "Enter the password:", parentStage, 400.0, 160.0);
                }
            });

            if (sPassword == null) {
                System.out.println("Entering password cancelled.");
                throw new Exception("Cancel");
            }
        } while (sPassword.equalsIgnoreCase(""));

Answer 1:

我建议一个包裹内的代码FutureTask对象。 FutureTask是在一个线程中执行的代码的一部分(通常是工人,你的情况的事件队列),并安全地取回它在另一个有用的一个结构(除其他事项外)。 FutureTask#get将阻塞,直到FutureTask#run已被调用,因此您的密码提示看起来是这样的:

final FutureTask query = new FutureTask(new Callable() {
    @Override
    public Object call() throws Exception {
        return queryPassword();
    }
});
Platform.runLater(query);
System.out.println(query.get());

作为FutureTask实现了Runnable,你可以直接将它传递给Platform#runLater(...) queryPassword()将在事件队列中inokved,并且直到该方法完成随后的调用来获取块。 当然,你会想在一个循环中调用此代码,直到密码确实匹配。



Answer 2:

重要

此代码是当你有代码是不是JavaFX应用程序线程上的特定情况下 ,你要调用的代码是JavaFX应用程序的线程来显示图形用户界面给用户,然后从继续处理之前GUI得到的结果关闭JavaFX应用程序线程。

您一定不能是JavaFX应用程序线程,当你在下面的代码片段调用CountdownLatch.await。 如果JavaFX应用程序线程调用CountDownLatch.await,你会死锁您的应用程序。 此外,如果您已经是JavaFX应用程序线程上,你不需要调用Platform.runLater执行JavaFX应用程序线程上的东西。

大多数时候,你知道,如果你是JavaFX应用程序的线程或不上。 如果你不知道,你可以通过调用检查你的线程Platform.isFxApplicationThread() 。


使用替代方法CountDownLatch 。 我喜欢Sarcan的方法虽然好;-)

final CountDownLatch latch = new CountDownLatch(1);
final StringProperty passwordProperty = new SimpleStringProperty();
Platform.runLater(new Runnable() {
    @Override public void run() {
        passwordProperty.set(queryPassword());
        latch.countDown();
    }
});
latch.await();      
System.out.println(passwordProperty.get());

下面是一些可执行代码示例演示使用的CountDownLatch的直到一个JavaFX对话已检索其然后可以由非JavaFX应用程序线程访问的结果,暂停非JavaFX应用程序线程的执行。

该应用程序阻止了JavaFX启动线程来自持续,直到用户在JavaFX的对话框中输入正确的密码应用。 没有显示已授权访问阶段,直到正确的密码已输入。

import javafx.application.*;
import javafx.beans.property.*;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.text.TextAlignment;
import javafx.stage.*;

import java.util.concurrent.CountDownLatch;

public class PasswordPrompter extends Application {
  final StringProperty passwordProperty = new SimpleStringProperty();
  @Override public void init() {
    final CountDownLatch latch = new CountDownLatch(1);

    Platform.runLater(new Runnable() {
      @Override public void run() {
        passwordProperty.set(new PasswordPrompt(null).getPassword());
        latch.countDown();
      }
    });

    try {
      latch.await();
    } catch (InterruptedException e) {
      Platform.exit();
    }

    System.out.println(passwordProperty.get());
  }

  @Override public void start(final Stage stage) {
    Label welcomeMessage = new Label("Access Granted\nwith password\n" + passwordProperty.get());
    welcomeMessage.setTextAlignment(TextAlignment.CENTER);

    StackPane layout = new StackPane();
    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20px;");
    layout.getChildren().setAll(welcomeMessage);
    stage.setScene(new Scene(layout));

    stage.show();
  }

  public static void main(String[] args) { launch(args); }
}

class PasswordPrompt {
  final Window owner;

  PasswordPrompt(Window owner) {
    this.owner = owner;
  }

  public String getPassword() {
    final Stage dialog = new Stage();
    dialog.setTitle("Pass is sesame");
    dialog.initOwner(owner);
    dialog.initStyle(StageStyle.UTILITY);
    dialog.initModality(Modality.WINDOW_MODAL);
    dialog.setOnCloseRequest(new EventHandler<WindowEvent>() {
      @Override public void handle(WindowEvent windowEvent) {
        Platform.exit();
      }
    });

    final TextField textField = new TextField();
    textField.setPromptText("Enter sesame");
    final Button submitButton = new Button("Submit");
    submitButton.setDefaultButton(true);
    submitButton.setOnAction(new EventHandler<ActionEvent>() {
      @Override public void handle(ActionEvent t) {
        if ("sesame".equals(textField.getText())) {
          dialog.close();
        }
      }
    });

    final VBox layout = new VBox(10);
    layout.setAlignment(Pos.CENTER_RIGHT);
    layout.setStyle("-fx-background-color: azure; -fx-padding: 10;");
    layout.getChildren().setAll(textField, submitButton);

    dialog.setScene(new Scene(layout));
    dialog.showAndWait();

    return textField.getText();
  }
}

上述程序将打印密码,屏幕和控制台纯粹是为了演示目的,显示或记录的密码是不是你会在实际应用做。



文章来源: Return result from javafx platform runlater