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.