Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait before Reacting to a Property Change JavaFX 8

Tags:

javafx-8

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.

like image 352
melkhaldi Avatar asked Dec 13 '25 11:12

melkhaldi


2 Answers

As Jeffrey pointed out, you can use ReactFX:

EventStreams.valuesOf(textField.textProperty())
        .successionEnds(Duration.ofSeconds(2))
        .subscribe(s -> doSomething(s));
like image 52
Tomas Mikula Avatar answered Dec 15 '25 09:12

Tomas Mikula


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 .

like image 31
Oliver Jan Krylow Avatar answered Dec 15 '25 11:12

Oliver Jan Krylow



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!