Luiz Roseiro Luiz Roseiro - 9 months ago 32
Java Question

How to run some code repeatedly after an interval that can changes?

I'm using a thread in JavaFX to repeat my code after an interval (initially 1s), but I want to be able change the interval that the thread is using to 500ms or 333ms based on user choice (I have a button in a menu bar to change for each choice). I did tried things like shutDown() if the user clicks on one of the buttons and initiate it again with the new value, but didn't work. Any ideas?

Here's the relevant part of my code:

ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(() -> {
//refresh users, line and "guiche"
updateFila(usuarios, guiches, fila);
updateGuiche(guiches, fila, graphicsContext);
turno++;
//ends the code after the end of the line
if (done) {
exec.shutdown();
}
}, 0, 1000, TimeUnit.MILLISECONDS); //This is interval that I need to change after user choice




I know that I'm executing scheduleAtFixedRate() right now, but it was just to see if the logic was fine.

Additionally, I need to pause, resume and reset the thread, all based on user click.

Answer Source

You could use a Timeline to execute a event handler every second and set the rate at which the animation runs to the number of times the update should happen per second, i.e. 2 or 3...

In the following example I use 5 instead of 3 for a more recognizable effect:

@Override
public void start(Stage primaryStage) {
    Line line = new Line(25, 125, 125, 125);
    Rotate rotate = new Rotate(0, 125, 125);
    line.getTransforms().add(rotate);

    ToggleButton btn = new ToggleButton();
    btn.textProperty().bind(Bindings.when(btn.selectedProperty()).then("5 Hz").otherwise("2 Hz"));
    StackPane.setAlignment(btn, Pos.BOTTOM_LEFT);

    // rotate by one 60th of a full rotation each time
    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), evt -> rotate.setAngle((rotate.getAngle() + (360d / 60d)) % 360)));
    timeline.setCycleCount(Animation.INDEFINITE);

    // rate depends on button state
    timeline.rateProperty().bind(Bindings.when(btn.selectedProperty()).then(5d).otherwise(2d));

    Pane linePane = new Pane(line);
    linePane.setMinSize(250, 250);
    linePane.setMaxSize(250, 250);
    StackPane root = new StackPane();
    root.getChildren().addAll(linePane, btn);

    Scene scene = new Scene(root, 300, 300);

    primaryStage.setScene(scene);
    timeline.play();

    primaryStage.show();
}

The binding is simply an example of setting the update frequency. You could of course use different means to assign this value, e.g.

ComboBox<Duration> combo = new ComboBox<>();
Duration initial = Duration.seconds(1);
combo.getItems().addAll(initial, Duration.seconds(1/3d), Duration.seconds(1/2d));
combo.setValue(initial);
combo.valueProperty().addListener((observable, oldValue, newValue) -> timeline.setRate(1/newValue.toSeconds()));