Kiraged
Kiraged

Reputation: 519

Javafx Not on fx application thread when using timer

I'm using this

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;



public class Main extends Application {

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

@Override
public void start(Stage primaryStage) {
    Group root = new Group();
    Scene scene = new Scene(root, 600, 400);
    primaryStage.setScene(scene);
    Circle circle = new Circle(300,200,50, Color.BLACK);
    primaryStage.setTitle("Circle");
    primaryStage.setResizable(false);
    root.getChildren().add(circle);
    moveCircle(circle, scene);
    primaryStage.show();
}
public int random(int min, int max) {
    return new Random().nextInt((max - min) + min);
}

public int random(int max) {
    return random(0, max);
}

public void moveCircle(Circle circle, Scene scene) {
    Platform.runLater(() -> {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                circle.setCenterX(random((int) scene.getX()));
                circle.setCenterY(random((int) scene.getY()));

            }
        }, 1000, 1000);
    });
}

But this:

public void moveCircle(Circle circle, Scene scene) {
    Platform.runLater(() -> {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                circle.setCenterX(random((int) scene.getX()));
                circle.setCenterY(random((int) scene.getY()));

            }
        }, 1000, 1000);
    });
}

Gives me this error:

Exception in thread "Timer-0" java.lang.IllegalStateException: Not on FX application thread; currentThread = Timer-0
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:204)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:364)
    at javafx.scene.Scene.addToDirtyList(Scene.java:485)
    at javafx.scene.Node.addToSceneDirtyList(Node.java:424)
    at javafx.scene.Node.impl_markDirty(Node.java:415)
    at javafx.scene.shape.Shape.impl_markDirty(Shape.java:942)
    at javafx.scene.shape.Circle$1.invalidated(Circle.java:136)
    at javafx.beans.property.DoublePropertyBase.markInvalid(DoublePropertyBase.java:112)
    at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:146)
    at javafx.scene.shape.Circle.setCenterX(Circle.java:122)
    at Main$2.run(Main.java:48)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)

And i really don't see what's wrong

Upvotes: 12

Views: 22761

Answers (1)

eckig
eckig

Reputation: 11134

It may be because you misunderstood how Platform.runLater() works..

The correct code snippet would be:

public void moveCircle(Circle circle, Scene scene) {
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            Platform.runLater(() -> {
                circle.setCenterX(random((int) scene.getX()));
                circle.setCenterY(random((int) scene.getY()));
            });
        }
    }, 1000, 1000);
}

But:

I would strongly recommend you to not use Timer but TimeLine instead! It is part of the JavaFX API and you do not have to do these Platform.runLater() calls. This is just quickly hacked together, but you get the idea:

public void moveCircle(Circle circle, Scene scene) {
    Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1), ev -> {
        circle.setCenterX(random((int) scene.getX()));
        circle.setCenterY(random((int) scene.getY()));
    }));
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();
}

Upvotes: 49

Related Questions