Make JavaFX wait and continue with code


Question

Basically I am trying to make a short effect using JavaFX. I have the shape of a heart (added together from two circles and a polygon) that I can vary in size using the double value p. "Standart Size" would be p = 1.0;.

I am trying to add a pumping effect to the heart. I have the method pumpOnce():

public void pumpOnce(){
    p = p + 1;
    initHeart();
    //Here goes what ever it takes to make stuff working!!
    p = p - 1;
    initHeart();
}

initHeart() draws the heart based on p.

I have found out that Thread.sleep(); or similar methods will not work due to the thread philosophy in JavaFX.

But what can I use instead?

1
9
10/19/2014 6:55:23 PM

Accepted Answer

The JavaFX animations are probably the way to go, but the "thread philosophy" in JavaFX isn't hard to work with if you want to roll your own, or do other, more complicated things in background threads.

The following code will pause and change the value in a label (full disclosure, I'm reusing code I wrote for another question):

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class HelloWorld extends Application {

    private static Label label;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Hello World!");
        label = new Label();
        label.setText("Waiting...");
        StackPane root = new StackPane();
        root.getChildren().add(label);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();

        Task<Void> sleeper = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
                return null;
            }
        };
        sleeper.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent event) {
                label.setText("Hello World");
            }
        });
        new Thread(sleeper).start();
    }
}

The basic JavaFX background tool is the Task, any JavaFX application that actually does anything will probably be littered with these all over. Learn how to use them.

14
10/19/2014 7:34:01 PM

Dave's solution is great for general purpose off thread based work in JavaFX.

If you wish to use the animation facilities of JavaFX, the solutions below demonstrate this using a Timeline or a ScaleTransition. The timeline implements a discrete scale of the UI element, so every quarter of a second the UI element is scaled larger or back to it's original size. The scale transition implements a smooth scale of the UI element, so the UI element gradually gets larger then smaller using an interpolated scale factor with the default easing interpolator.

import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class BeatingHeart extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    public void start(Stage stage) {
        ImageView heart = new ImageView(HEART_IMAGE_LOC);

        animateUsingTimeline(heart);
//        animateUsingScaleTransition(heart);

        StackPane layout = new StackPane(heart);
        layout.setPrefWidth(heart.getImage().getWidth() * 2);
        layout.setPrefHeight(heart.getImage().getHeight() * 2);

        Scene scene = new Scene(layout);
        stage.setScene(scene);
        stage.show();
    }

    private void animateUsingTimeline(ImageView heart) {
        DoubleProperty scale = new SimpleDoubleProperty(1);
        heart.scaleXProperty().bind(scale);
        heart.scaleYProperty().bind(scale);

        Timeline beat = new Timeline(
            new KeyFrame(Duration.ZERO,         event -> scale.setValue(1)),
            new KeyFrame(Duration.seconds(0.5), event -> scale.setValue(1.1))
        );
        beat.setAutoReverse(true);
        beat.setCycleCount(Timeline.INDEFINITE);
        beat.play();
    }

    private void animateUsingScaleTransition(ImageView heart) {
        ScaleTransition scaleTransition = new ScaleTransition(
            Duration.seconds(1), heart
        );
        scaleTransition.setFromX(1);
        scaleTransition.setFromY(1);
        scaleTransition.setFromZ(1);
        scaleTransition.setToX(1.1);
        scaleTransition.setToY(1.1);
        scaleTransition.setToZ(1.1);
        scaleTransition.setAutoReverse(true);
        scaleTransition.setCycleCount(Animation.INDEFINITE);
        scaleTransition.play();
    }

    private static final String HEART_IMAGE_LOC =
            "http://icons.iconarchive.com/icons/mirella-gabriele/valentine/128/Heart-red-icon.png";
    // icon obtained from: http://www.iconarchive.com/show/valentine-icons-by-mirella-gabriele/Heart-red-icon.html
    // icon license: Free for non-commercial use, commercial use not allowed.
}

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon