【问题标题】:Wait for and then receive textfield input without freezing GUI等待然后接收文本字段输入而不冻结 GUI
【发布时间】:2015-07-24 00:26:16
【问题描述】:

我希望我没有重复问题,但我找不到专门针对我的问题的问题。 我正在开发一个小型数学闪存卡应用程序,使用 JavaFX 创建 GUI。该程序应按如下方式运行:

  1. 用户选择设置,然后按开始按钮。
  2. gui 显示问题和文本字段供用户输入。
  3. 用户在 X 秒内输入答案或 gui 自动移至下一个问题 - 或者,用户可以通过按 next 按钮立即移至下一个问题。
  4. GUI 显示分数和平均值。

问题是用户文本字段中的getText() 在按下开始 按钮后立即被处理,而没有给用户输入答案的机会。在处理用户的答案之前,如何让程序等待 X 秒或单击 next 按钮?这是我的代码:

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

这是问题代码……至少我是这么看的。

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

我尝试过使用Thread.sleep(X),但无论我指定多久都会冻结gui,然后在进入测试屏幕之前通过addAsk() 方法和循环。 (我知道是因为我设置了程序来打印问题并回答输入到控制台)。它显示了最后一个问题,仅此而已。

我没有包含 next 按钮代码,因为无论如何我都无法让 gui 转到测试页面。

感谢您对任何代码的任何帮助。

【问题讨论】:

    标签: java multithreading user-interface javafx freeze


    【解决方案1】:

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

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

    这是一个如何使用它来实现类似结果的示例。

    完整代码

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

    【讨论】:

      【解决方案2】:

      对于计时器,您应该(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 方法之间的输入中捕获文本。

      【讨论】:

      • 您的回答存在一些误解。您可能希望将 allowedTime 设置为 5 秒而不是 500 秒。 progressProperty 应该是一个介于 0 和 1 之间的数字,因此您不应该将它双向绑定到从 0 到 100 的值。这里首选单向绑定:progressBar.progressProperty().bind(percentOfTimeUsed.divide(100.0));
      • @jewelsea 这解释了允许时间的问题 - 感谢您指出这一点。更新示例。
      • 正是我想要的。谢谢。
      猜你喜欢
      • 1970-01-01
      • 2013-07-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-05
      • 1970-01-01
      • 2022-08-20
      相关资源
      最近更新 更多