Modifying JavaFX gui from different thread in diff

2020-02-14 07:35发布

问题:

I am trying to create an environment, in which a GUI is set up, and while the user is modifying its components through listeners, another thread is adding new elements to it every X seconds. (it is a game, yes)

I want the game to be in one class, and the "element adder" thread in another - but of course Java complains that I cannot modify JavaFX GUI from another thread.

I've seen solutions using Platform.runLater() but as far as I see, all these solutions use anonymous inner classes. And I really want my adder to be in another (named) class.

Any hints would be really appreciated, here's a minimal version of the code, that reproduces the problem:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class DifferentThreadJavaFXMinimal extends Application {

    private Thread t;
    private GridPane gp;

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        gp = new GridPane();
        Scene gameScene = new Scene(gp, 600,500);
        primaryStage.setScene(gameScene);

        t = new Thread(new Thread2(instance()));




        primaryStage.show();
        t.start(); 
    }
    void addElement(int i) {
        Button actualButton = new Button("TEST");
        gp.add(actualButton,i,i);
    }
    public DifferentThreadJavaFXMinimal instance(){
        return this;
    }

}

class Thread2 implements Runnable {
    private DifferentThreadJavaFXMinimal d;

    Thread2(DifferentThreadJavaFXMinimal dt){
        d=dt;
    }

    @Override
    public void run() {
        for (int i=0;i<4;i++) {
        d.addElement(i);
        }
    }

}

回答1:

You can still use your standalone class.

The rule is that changes to the UI must happen on the FX Application Thread. You can cause that to happen by wrapping calls from other threads that change the UI in Runnables that you pass to Platform.runLater(...).

In your example code, d.addElement(i) changes the UI, so you would do:

class Thread2 implements Runnable {
    private DifferentThreadJavaFXMinimal d;

    Thread2(DifferentThreadJavaFXMinimal dt){
        d=dt;
    }

    @Override
    public void run() {
        for (int i=0;i<4;i++) {
            final int value = i ;
            Platform.runLater(() -> d.addElement(value));
            // presumably in real life some kind of blocking code here....
        }
        System.out.println("Hahó");
    }

}