Wait for and then receive textfield input without freezing GUI

时间:2015-05-12 22:32:53

标签: java multithreading user-interface javafx freeze

I hope I'm not duplicating a question, but I couldn't find one specifically for my issue. I'm developing a small math flash card application, using JavaFX to create the GUI. The program should runs as follow:

  1. user selects settings, then presses start button.
  2. gui displays question and textfield for user input.
  3. user inputs answer within X amount of seconds or gui automatically move onto the next question - alternatively, user can move onto next question immediately by pressing next button.
  4. GUI displays score and average.

The problems is SELECT A.COLUMNA, A.COLUMNB, A.COLUMNC, B.COLUMND FROM TABLE1 A LEFT JOIN TABLE2 B ON A.COLUMNA = B.COLUMNA AND A.COLUMNB = B.COLUMNB from user textfield is processed as soon as start button is pressed, without giving the user a chance to enter an answer. How do I make the program wait for X amount of seconds or for the next button to be clicked before processing the user's answer? Here's my code:

getText()

Here is the problem code... at least how I see it.

//start button changes view and then runs startTest()
start.setOnAction(e -> {

        setLeft(null);
        setRight(null);

        setCenter(test_container);
        running_program_title.setText(getDifficulty().name() + " Test");
        buttons_container.getChildren().clear();
        buttons_container.getChildren().addAll(next, quit, submit);

        startTest();
    });

I've tried using //startTest method calls askAdd() to ask an addition question void startTest() { int asked = 0; int correct = 0; while (asked < numberOfQuestions) { if(askAdd()){ correct++; asked++; } } boolean askAdd() { int a = (int) (Math.random() * getMultiplier()); int b = (int) (Math.random() * getMultiplier()); //ask question question.setText("What is " + a + " + " + b + "?"); //code needed to pause method and wait for user input for X seconds //retrieve user answer and return if its correct return answer.getText().equalsIgnoreCase(String.valueOf(a+b)); } but that freezes the gui for however long I specify and then goes through the Thread.sleep(X) method and the loop before going to the test screen. (I know because I had the program set up to print the questions and answer input to the console). It shows the last question and that's all.

I didn't include the next button code because I can't get the gui to go to the test page anyway.

Any help on any of the code is appreciated.

2 个答案:

答案 0 :(得分:1)

这可以通过各种方法实现。

PauseTransition是目前众多合适的解决方案之一。它等待X time interval,然后执行Task。它可以startrestartstop at any moment

以下是如何使用它来实现类似结果的示例。

enter image description here

完整代码

import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.stream.IntStream;

public class Main extends Application {

    int questionIndex = 0;
    int noOfQuestions = 10;

    @Override
    public void start(Stage stage) {

        VBox box = new VBox(10);
        box.setPadding(new Insets(10));
        Scene scene = new Scene(new ScrollPane(box), 500, 200);
        ObservableList<String> questions =
                FXCollections.observableArrayList("1) Whats your (full) name?",
                                                  "2) How old are you?",
                                                  "3) Whats your Birthday?",
                                                  "4) What starsign does that make it?",
                                                  "5) Whats your favourite colour?",
                                                  "6) Whats your lucky number?",
                                                  "7) Do you have any pets?",
                                                  "8) Where are you from?",
                                                  "9) How tall are you?",
                                                  "10) What shoe size are you?");

        ObservableList<String> answers = FXCollections.observableArrayList();

        final PauseTransition pt = new PauseTransition(Duration.millis(5000));

        Label questionLabel = new Label(questions.get(questionIndex));
        Label timerLabel = new Label("Time Remaining : ");
        Label time = new Label();
        time.setStyle("-fx-text-fill: RED");
        TextField answerField = new TextField();

        Button nextQuestion = new Button("Next");

        pt.currentTimeProperty().addListener(new ChangeListener<Duration>() {
            @Override
            public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
                time.setText(String.valueOf(5 - (int)newValue.toSeconds()));
            }
        });

        box.getChildren().addAll(questionLabel, answerField, new HBox(timerLabel, time), nextQuestion);

        nextQuestion.setOnAction( (ActionEvent event) -> {
            answers.add(questionIndex, answerField.getText());

            //Check if it is the last question
            if(questionIndex == noOfQuestions-1) {
                pt.stop();
                box.getChildren().clear();
                IntStream.range(0, noOfQuestions).forEach(i -> {
                    Label question = new Label("Question : " + questions.get(i));
                    question.setStyle("-fx-text-fill: RED");
                    Label answer = new Label("Answer : " + answers.get(i));
                    answer.setStyle("-fx-text-fill: GREEN");
                    box.getChildren().addAll(question, answer);
                });
            }
            // All other time
            else {
                //Set new question
                questionLabel.setText(questions.get(++questionIndex));
                answerField.clear();
                pt.playFromStart();
            }
        });

        pt.setOnFinished( ( ActionEvent event ) -> {
            nextQuestion.fire();
        });
        pt.play();

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

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

答案 1 :(得分:0)

对于计时器,您应该(IMO)使用Timeline。这是一个例子:

public class MultiGame extends Application {
    ProgressBar progressBar;

    final int allowedTime = 5; //seconds
    final DoubleProperty percentOfTimeUsed = new SimpleDoubleProperty(0);
    final Timeline timer =
            new Timeline(
                new KeyFrame(
                        Duration.ZERO, new KeyValue(percentOfTimeUsed, 0)),
                new KeyFrame(
                        Duration.seconds(allowedTime), new KeyValue(percentOfTimeUsed, 1))
            );

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

    @Override
    public void start(Stage primaryStage) {
        BorderPane root = new BorderPane();

        progressBar = new ProgressBar();
        progressBar.progressProperty().bindBidirectional(percentOfTimeUsed);
        root.setTop(progressBar);

        Button answer = new Button("Answer");
        answer.setOnAction(ae -> restart());// the on answer handler

        Button skip = new Button("Skip");
        skip.setOnAction(ae -> restart());// the skip question handler

        HBox mainContent = new HBox(15,
                new Label("Your Question"), new TextField("The answer"),  answer, skip);
        root.setCenter(mainContent);

        timer.setOnFinished(ae -> restart());// the end of timer handler

        primaryStage.setScene(new Scene(root));
        primaryStage.show();

        restart();
    }

    void restart() { timer.stop(); timer.playFromStart(); }

    void pause() { timer.pause(); }

    void resume() { timer.play(); }
}

您只需要在时间轴的开头和restart方法之间捕获输入中的文本。