Is there a way to keep listening to a property change, for a few seconds, then fire an event (call a method)?
For example, when the user enter data in a text field:
textField.textProperty().addListener(new ChangeListener<String>() {
@Override
public void changed(ObservableValue<? extends String> arg0, String arg1, String arg2) {
//before calling a method to do something.. wait for a few seconds ...
}
});
A scenario would be firing an action based on the string value. For example, hitting "M" for move, or "MA" for mask. I would like to "keep listening" for 2 seconds before making an action.
As Jeffrey pointed out, you can use ReactFX:
EventStreams.valuesOf(textField.textProperty())
.successionEnds(Duration.ofSeconds(2))
.subscribe(s -> doSomething(s));
There are a few ways to solve this.
Usually I would recommend the Java Timer API, but if by "making an action" you imply updating stuff on the FX thread, you would have to synchronize the threads, which is bothersome.
Depending on your use case, you could instead use Transitions or the Timeline in FX.
Here is an example with a transition:
package application;
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TransitionedInputExample extends Application {
@Override
public void start(final Stage stage) {
final ImageView spinner =
new ImageView("https://cdn1.iconfinder.com/data/icons/duesseldorf/16/process.png");
spinner.setVisible(false);
final Label title = new Label("Timed Action Commander Example", spinner);
title.setContentDisplay(ContentDisplay.BOTTOM);
title.setFont(Font.font("Helvetica", FontWeight.BOLD, FontPosture.REGULAR, 16));
final TextField textInput = new TextField();
textInput.setPromptText("Enter command");
final TextArea textOutput = new TextArea();
textOutput.setPromptText("Command results will show up here");
final VBox layout = new VBox(title, textInput, textOutput);
layout.setSpacing(24);
// setup some transition that rotates an icon for 2 seconds
final RotateTransition rotateTransition = new RotateTransition(Duration.seconds(1), spinner);
rotateTransition.setByAngle(90);
// delay rotation so that user can type without being distracted at once
rotateTransition.setDelay(Duration.seconds(1));
// restart transition on user input
textInput.textProperty().addListener((observable, oldText, newText) -> {
spinner.setVisible(true);
rotateTransition.playFromStart();
});
rotateTransition.setOnFinished((finishHim) -> {
// execute command
textOutput.setText("Executing " + textInput.getText());
spinner.setVisible(false);
});
final Scene scene = new Scene(layout);
stage.setScene(scene);
stage.show();
}
public static void main(final String[] args) {
launch(args);
}
}
For a solutition using a Timeline see this post .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With