简体   繁体   中英

wait for a task to be completed without blocking the UI in javafx

I am trying to create a quiz application using JavaFX, as I am invoking questions using

Q1.invoke();
Q2.invoke();

these questions will be displayed on the UI thread

public void display(McqQuestion mcqQuestion) {
        resourceAsStream  = getClass().getResourceAsStream("/mcqview.fxml");
        fxmlLoader = new FXMLLoader();
        if (executorService==null) executorService =Executors.newSingleThreadExecutor();
        Parent root = null;
        try {
            root = fxmlLoader.load(resourceAsStream);
            Mcqview controller = fxmlLoader.getController();
            controller.setAnswer1(mcqQuestion.getAnswers().get(0));
            //controller class has setters to accept question properties.
            controller.multipleChoiceQuestionType = this;
            this.view.getBorderPane().setCenter(root);
}

once the question is displayed I need to wait until I get an answer, if I didn't get an answer the next question should be invoked.so I introduced a thread inside the display method to wait for a timeout

submit = executorService.submit(() -> {
             try {
                    TimeUnit.SECONDS.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             });

            try {
                submit.get(20,TimeUnit.SECONDS);
                System.out.println("waiting finished");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

since the future.get(); is a blocking call it blocks the UI thread too, how to achieve this without blocking the UI thread.

Don't use a seperate thread for this purpose. This just makes things harder. JavaFX provides ways of waiting that do not require you to bother with concurrency issues.

In this case waiting can be done from a PauseTransition with an onFinished handler. Handle a answer from an event handler for the user input.

private static class Question {

    private final String questionText;
    private final String answers[];
    private final int correctAnswerIndex;

    public Question(String questionText, String[] answers, int correctAnswerIndex) {
        if (answers.length != 3) {
            // for simplicity's sake allow only exactly 3 answers
            throw new IllegalArgumentException();
        }
        this.questionText = questionText;
        this.answers = answers;
        this.correctAnswerIndex = correctAnswerIndex;
    }

}

private VBox questionPane;
private Label questionText;
private Button[] answerButtons;
private PauseTransition pauseTransition;
private Question currentQuestion;

private void answer(int index) {
    pauseTransition.stop(); // no longer wait for timeout
    Alert alert = new Alert(Alert.AlertType.INFORMATION);
    alert.setContentText((index == currentQuestion.correctAnswerIndex)
            ? "correct answer"
            : "incorrect answer");

    // show result and exit
    alert.showAndWait();
    Platform.exit();
}

private void ask(Question question) {
    questionText.setText(question.questionText);
    for (int i = 0; i < 3; i++) {
        answerButtons[i].setText(question.answers[i]);
    }
    currentQuestion = question;
    pauseTransition.playFromStart(); // start timeout timer
}

private void timeout() {
    pauseTransition.stop();
    Alert alert = new Alert(Alert.AlertType.INFORMATION);
    alert.setContentText("your time ran out");

    // cannot use showAndWait form animation directly
    Platform.runLater(() -> {
        // show result and exit
        alert.showAndWait();
        Platform.exit();
    });
}

@Override
public void start(Stage stage) {
    pauseTransition = new PauseTransition(Duration.seconds(10));
    pauseTransition.setOnFinished(evt -> timeout());

    questionText = new Label();
    questionText.setWrapText(true);

    questionPane = new VBox(questionText);
    questionPane.setPrefSize(400, 400);
    answerButtons = new Button[3];

    for (int i = 0; i < 3; i++) {
        final int answerIndex = i;
        Button button = new Button();
        button.setOnAction(evt -> answer(answerIndex));
        answerButtons[i] = button;
        questionPane.getChildren().add(button);
    }

    Scene scene = new Scene(questionPane);

    stage.setScene(scene);
    stage.show();

    Question question = new Question(
            "What is the answer to the ultimate question of life, the universe, and everything?",
            new String[]{"Mew", "42", "Peanut butter"},
            1
    );
    ask(question);
}

You could easily implement the timeout or the result of answering a question in a different way, eg by asking the next question or showing the results when the last question is done.

For changes in UI you should use

Platform.runLater(() -> {

});

and for Thread you should use:

Task<Void> task = new Task<Void>() {
    @Override
    protected Void call() throws Exception {
                return null;
        }
};

and pass task object to

executorService.submit(task)

hope it will be helpful

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM