user3435407
user3435407

Reputation: 1067

How to wait for user's action in JavaFX (in the same stage)

Do you know how to wait for the user's input in a for loop? I don't mean the showAndWait() method, because I am not opening a new dialogue stage for the user. So for example, each round of the for loop should be waiting for the user to push a button before going ahead with the next round.

How is it possible? Many thanks!

UPDATE:

Now it came to my mind, that it would work with a while(buttonNotPressed){} but is it a good solution? I mean the while loop is running in this case as crazy until the user won't push the button. Or doest it work somehow similarly with wait methods?

Imagine it as a session: User starts session with handleStart() You give the user 5 questions, one after one. In every iteration, the user can answer the upcoming question and he can save or submit the answer by handleSaveButton() You process the answer as you want, and go ahead with the next iteration. The point is, that the iteration must stop, until the save button hasn't been pressed.

Upvotes: 1

Views: 12187

Answers (4)

Luke Palmer
Luke Palmer

Reputation: 21

ALTERNATIVE SOLUTION W/O PAUSING:

I'm creating a game where I want the user to pick the game difficulty before the game starts. Instead of trying to pause the program midway through, I just put the next step of the code in a separate method which you call once a button is clicked:

private static difficulty;

public static void main(String[] args) {
    try {
        Application.launch(args);
    } catch (UnsupportedOperationException e) {
    }
}

public void start(Stage startStage) {
    HBox buttons = new HBox();
    Button easyButton = new Button("Easy");
    Button mediumButton = new Button("Medium");
    Button hardButton = new Button("Hard");
    buttons.getChildren().addAll(easyButton, mediumButton, hardButton);
    buttons.setAlignment(Pos.CENTER);
    hbox.getChildren().addAll(buttons);
    Scene startScene = new Scene(buttons, 200, 200);
    startStage.setScene(startScene);
    startStage.show(); // MENU

    EventHandler<ActionEvent> playEasy = new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            difficulty = 1; // SET DIFFICULTY
            startStage.close(); // CLOSE MENU
            play(); // RUN GAME ON EASY
        }
    };
    EventHandler<ActionEvent> playMedium = new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            difficulty = 2; // SET DIFFICULTY
            startStage.close(); // CLOSE MENU
            play(); // RUN GAME ON MEDIUM
        }
    };
    EventHandler<ActionEvent> playHard = new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent actionEvent) {
            difficulty = 3; // SET DIFFICULTY
            startStage.close(); // CLOSE MENU
            play(); // RUN GAME ON HARD
        }
    };
    easyButton.setOnAction(playEasy);
    mediumButton.setOnAction(playMedium);
    hardButton.setOnAction(playHard);
}

public void play() {
    // WRITE GAME CODE HERE
}

To solve your specific problem, you could probably pass the startStage into the play method and then just update the scene there...but regardless I do hope this helps someone whos having trouble on how to use buttons! :)

Upvotes: 0

Lucas Lombardi
Lucas Lombardi

Reputation: 129

In my case, for inside for, had to create 2 index in class, use:

//start method
Timer timer = new Timer();
TimerTask task = new TimerTask()
            {
                    public void run()
                    {
                        Platform.runLater(()->{
                            //... code to run after time, calling the same mehtod, with condition to stop
                        });

                    }
            };          
timer.schedule(task, time); 
//end method

Had to use recursive method, incrementing the index with conditions, cause the tasks were been schedule all at the same time, without wait time. I do not know if it is rigth, but was the solution that i found. Hope it helps.

Upvotes: 0

Toby
Toby

Reputation: 110

I recently ran into a similar problem where I wanted something to be executed with an interval (if that's what you mean), until the user fired an event. I found 3 ways to do this:

UPDATE

You should use the stop/cancel method for the custom runnable and timer or else the thread will still be running when you exit the application. Timeline seems do it by itself.

Using a Timer:

Timer timer = new Timer();
TimerTask task = new TimerTask() {
    @Override
    public void run() {
        System.out.println("Printed every second.");
    }
};
timer.scheduleAtFixedRate(task, 0, 1000);
//timer.cancel();

With a TimeLine:

Timeline tl = new Timeline(new KeyFrame(Duration.millis(1000), e -> {
    System.out.println("Timeline");
}));

tl.setCycleCount(Timeline.INDEFINITE);
tl.play();
//tl.stop();

Or making your own runnable class:

public class Runner implements Runnable {
    private final Thread thread = new Thread(this);
    private boolean run;

    @Override
    public void run() {
        while(run) {
            try {
                System.out.println("Printed from loop");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                run = false;
            }
        }
    }

    public void start() {
        run = true;
        thread.start();
    }

    public void stop() {
        if(run) {
            thread.interrupt();
            System.out.print("Thread has stopped.");
        }
    }
}

And then when a person clicks fx. a button the event would stop using the example James_D posted:

Button btn = new Button("Button");

btn.setOnAction(e -> {
    timer.cancel();
    tl.stop();
    runner.stop();
});

Upvotes: 1

James_D
James_D

Reputation: 209330

Don't do it like that. The FX toolkit, like any event-driven GUI toolkit, already implements a loop for the purposes of rendering the scene graph and processing user input each iteration.

Just register a listener with the button, and do whatever you need to do when the button is pressed:

button.setOnAction(event -> {
    // your code here...
});

If you want the action to change, just change the state of some variable each time the action is performed:

private int round = 0 ;

// ...

button.setOnAction(event -> {
    if (round < 5) {
        System.out.println("Round "+round);
        System.out.println("User's input: "+textArea.getText());
        round++ ;
    }
});

Upvotes: 3

Related Questions