简体   繁体   English

等待然后接收文本字段输入而不冻结GUI

[英]Wait for and then receive textfield input without freezing GUI

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. 我正在开发一个小型数学闪存卡应用程序,使用JavaFX创建GUI。 The program should runs as follow: 该计划应如下运行:

  1. user selects settings, then presses start button. 用户选择设置,然后按开始按钮。
  2. gui displays question and textfield for user input. gui显示用户输入的问题和文本字段。
  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. 用户在X秒内输入答案或gui自动移动到下一个问题 - 或者,用户可以通过按下一个按钮立即转到下一个问题。
  4. GUI displays score and average. GUI显示得分和平均值。

The problems is getText() from user textfield is processed as soon as start button is pressed, without giving the user a chance to enter an answer. 问题是来自用户文本字段的getText()在按下开始按钮时立即处理,而不给用户输入答案的机会。 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? 在处理用户的答案之前,如何让程序等待X秒或者单击下一个按钮? Here's my code: 这是我的代码:

//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();
    });

Here is the problem code... at least how I see it. 这是问题代码......至少我是怎么看的。

//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));
}

I've tried using Thread.sleep(X) but that freezes the gui for however long I specify and then goes through the addAsk() method and the loop before going to the test screen. 我已经尝试过使用Thread.sleep(X) ,但是无论我指定多久都会冻结gui,然后在进入测试屏幕之前通过addAsk()方法和循环。 (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. 我没有包含下一个按钮代码,因为无论如何我都无法让gui进入测试页面。

Any help on any of the code is appreciated. 任何代码的任何帮助表示赞赏。

This can be achieved by various methods. 这可以通过各种方法实现。

PauseTransition is one of the many apt solution present. PauseTransition是目前众多的解决方案之一。 It waits for X time interval and then performs a Task . 它等待X time interval ,然后执行Task It can start , restart , stop at any moment . 它可以随时startrestartstop at any moment

Here is an example of how it can used to achieve a similar result. 以下是如何使用它来实现类似结果的示例。

在此输入图像描述

Complete Code 完整代码

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);
    }
}

For the timer you should (IMO) use a Timeline . 对于计时器,您应该(IMO)使用Timeline Here is an example: 这是一个例子:

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(); }
}

You just need to capture the text from the input in between the starting of the timeline and the restart method. 您只需要在时间轴的开始和restart方法之间捕获输入中的文本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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