Вернуть результат из runlater платформы javafx
Я работаю над приложением JavaFX, в моем сценарии - показать запрос пароля, созданный в JavaFX, который принимает пароль с двумя вариантами OK
а также Cancel
, Я вернул пароль, введенный пользователем.
Мой класс показа пароля -
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;
}
Мой код для отображения подсказки пароля приведен ниже, фактически эта подсказка будет отображаться поверх другого пользовательского интерфейса, поэтому мне нужно включить ее внутрь 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(""));
2 ответа
Я бы порекомендовал обернуть код в 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()
будет вставлен в очередь событий и последующий вызов блока get до завершения этого метода. Конечно, вы захотите вызывать этот код в цикле, пока пароль фактически не совпадет.
Важный
Этот код предназначен для особого случая, когда у вас есть код, который не находится в потоке приложения JavaFX, и вы хотите вызвать код, который находится в потоке приложения JavaFX, для отображения GUI для пользователя, а затем получить результат из этого GUI перед продолжением обработки от потока приложения JavaFX.
Вы не должны быть в потоке приложения JavaFX при вызове CountdownLatch.await в приведенном ниже фрагменте кода. Если вы вызовете CountDownLatch.await в потоке JavaFX-приложения, вы заблокируете приложение. Кроме того, если вы уже находитесь в потоке приложения JavaFX, вам не нужно вызывать Platform.runLater для выполнения чего-либо в потоке приложения JavaFX.
Большую часть времени вы знаете, находитесь ли вы в потоке приложений JavaFX или нет. Если вы не уверены, вы можете проверить свой поток, вызвав Platform.isFxApplicationThread ().
Альтернативный метод с использованием CountDownLatch. Мне больше нравится метод Саркана;-)
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();
}
}
Вышеупомянутая программа выводит пароль на экран и консоль исключительно для демонстрационных целей, отображение или регистрация паролей - это не то, что вы бы делали в реальном приложении.