Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to wait for a transition to end in javafx 2.1?

My scene consists only of an ImageView, displaying an image. I would like to fade the image to black (assigned color of the scene), then after some time, fade from black to the image again. I found the FadeTransition very fitting for this purpose. This is a piece of my code:

    // fade to black transition
    FadeTransition ft1 = new FadeTransition(Duration.millis(2000), myImageView);
    ft1.setFromValue(1.0);
    ft1.setToValue(0.0);
    ft1.play();

    // fade from black transition
    FadeTransition ft2 = new FadeTransition(Duration.millis(2000), myImageView);
    ft2.setFromValue(0.0);
    ft2.setToValue(1.0);
    ft2.play();

My problem is that ft1.play() is asynchronous, so the code below will start being executed before ft1.play() is exited. As the result I see only the second transition. How can I wait for the first transition to end and then to launch the second transition? I cannot put the thread to sleep in between because it's the main javafx thread (tried and didn't work).

I tried using the onFinishedProperty() method with the combination of a busy-waiting on a flag, but I get stuck in the while loop forever. Here is my code for that:

    boolean isTransitionPlaying;
    FadeTransition ft = new FadeTransition(Duration.millis(2000), iv);
    ft.setFromValue(1.0);
    ft.setToValue(0.0);
    ft.onFinishedProperty().set(new EventHandler<ActionEvent>() {
        @Override 
        public void handle(ActionEvent actionEvent) {
            transitionPlaying = false;
        }
    });
    transitionPlaying = true;
    ft.play();

    while (transitionPlaying == true)
    {
        // busy wait
        System.out.println("still waiting...");
    }

    FadeTransition ft2 = new FadeTransition(Duration.millis(2000), iv);
    ft2.setFromValue(0.0);
    ft2.setToValue(1.0);
    ft2.play();

How is waiting done properly? Thank you

like image 604
dadox Avatar asked Jun 25 '12 10:06

dadox


3 Answers

Ok if your ft2 is the reflective animation of ft1 then do

ft1.setAutoReverse(true);
ft1.setCycleCount(1);
// Or
// ft1.setCycleCount(Timeline.INDEFINITE);
// to loop infinitely (blinking effect) until stop()

and you don't ft2. If you still need ft2 to play after ft1 then

ft1.setOnFinished(new EventHandler<ActionEvent>() {

    @Override
    public void handle(ActionEvent event) {
        ft2.play();
    }
});
like image 161
Uluk Biy Avatar answered Oct 25 '22 00:10

Uluk Biy


Busy waiting (or even Thread.sleep) on the JavaFX application thread is always a bad idea - you tie up the thread which handles the UI processing so your transitions, as well as the rest of your UI, is never updated - effectively freezing your app UI for the duration of the busy wait. For a responsive UI, you need to run your logic on the FX application thread as quickly as possible, then let the thread go so the rest of the JavaFX system can get on with it's processing. This is why the transitions have async callbacks - which, once you get used to them, are a very natural way of developing.

In addition to Uluk's solutions (which are great), you could also look at the SequentialTransition class for handling assistance in performing transitions in sequence. Note that if you want to take an action after the SequentialTransition has completed, you will still want to add an onFinished handler to the SequentialTransition to take action at that time.

like image 4
jewelsea Avatar answered Oct 25 '22 00:10

jewelsea


I had problem where other code was doing some calculations and I wanted to run Animations in JavaFX app, but needed to make other code to wait for animation to finish. I wasn't able to tell this other code when animation has finished, so I have created method for playing Animation and then waiting for it to finish:

    private synchronized void playAnimationAndWaitForFinish(final Animation animation) {
    if (Platform.isFxApplicationThread()) {
        throw new IllegalThreadStateException("Cannot be executed on main JavaFX thread");
    }
    final Thread currentThread = Thread.currentThread();
    final EventHandler<ActionEvent> originalOnFinished = animation.getOnFinished();
    animation.setOnFinished(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event) {
            if (originalOnFinished != null) {
                originalOnFinished.handle(event);
            }
            synchronized (currentThread) {
                currentThread.notify();
            }
        }
    });
    Platform.runLater(new Runnable() {

        @Override
        public void run() {
            animation.play();
        }
    });
    synchronized (currentThread) {
        try {
            currentThread.wait();
        } catch (InterruptedException ex) {
            //somebody interrupted me, OK
        }
    }
}

It is required that this method is not invoked in main JavaFX thread, otherwise, it works for me.

like image 1
user3727126 Avatar answered Oct 24 '22 23:10

user3727126