使用其他线程更改GridPane中的项目

时间:2018-11-13 00:15:43

标签: java multithreading javafx

我有一个带有图像的GridPane。我希望图像每2秒更改一次位置(转到另一个网格子节点)。但是,当我使用另一个线程运行程序时,我得到:不存在FX应用程序线程错误。我们将提供任何帮助!

我也尝试过使用Platform.runLater,但仍然无法正常工作。通过阅读不同的文章,我了解到GridPane不是线程安全的。有办法绕过吗?

public class BoardController implements Initializable {
    @FXML
    private GridPane board;
    @FXML
    private Text position;

    String pikachuPos = "";
    String pokeballPos = "";
    int i = 0;
    int j = 0;
    boolean selectPikachu = true;
    int count = 0;

    int pikachuRow = 0;
    int pikachuCol = 0;
    int pokeballRow = 0;
    int pokeballCol = 0;

    ImageView pikachu = new ImageView(getClass().getResource("pikachu.png").toExternalForm());
    ImageView pokeball = new ImageView(getClass().getResource("pokeball.png").toExternalForm());

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        pikachu.setFitWidth(30);
        pikachu.setFitHeight(30);
        pokeball.setFitWidth(30);
        pokeball.setFitHeight(30);

        position.setVisible(true);
        if (count == 2) {
            System.out.println("2");
        }
        boolean pikachu = true;

        Button[][] buttons = new Button[10][10];
        Button b;
        for (i = 0; i < 10; i++) {
            for (j = 0; j < 10; j++) {
                b = new Button(j + " " + i);
                buttons[i][j] = b;
                buttons[i][j].setOnAction((event) -> {
                    if (this.selectPikachu) {
                        Button button = (Button) event.getSource();
                        this.pikachuPos = button.getText();
                        this.selectPikachu = false;
                        this.position.setText("Choose Pokeball position");
                    } else {
                        Button button = (Button) event.getSource();
                        this.pokeballPos = button.getText();
                        this.position.setText("");
                        try {
                            this.play();
                        } catch (InterruptedException ex) {
                            Logger.getLogger(BoardController.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }

                });

                board.add(buttons[i][j], i, j);
                GridPane.setHalignment(buttons[i][j], HPos.CENTER);
            }
        }
    }

    public void play() throws InterruptedException {

        Node node = board.getChildren().get(0);
        board.getChildren().clear();
        board.getChildren().add(0, node);

        String[] pikachuDetails = pikachuPos.split(" ");
        String[] pokeballDetails = pokeballPos.split(" ");

        pikachuRow = Integer.parseInt(pikachuDetails[1]);
        pikachuCol = Integer.parseInt(pikachuDetails[0]);

        pokeballRow = Integer.parseInt(pokeballDetails[1]);
        pokeballCol = Integer.parseInt(pokeballDetails[0]);

        ImageView pokeball = new ImageView(getClass().getResource("pokeball.png").toExternalForm());
        pokeball.setFitWidth(30);
        pokeball.setFitHeight(30);

        ImageView pikachu = new ImageView(getClass().getResource("pikachu.png").toExternalForm());
        pikachu.setFitWidth(30);
        pikachu.setFitHeight(30);

        GridPane.setHalignment(pokeball, HPos.CENTER);
        board.add(pokeball, pokeballRow, pokeballCol);

        GridPane.setHalignment(pikachu, HPos.CENTER);
        board.add(pikachu, pikachuRow, pikachuCol);

        startTask();

    }

    public void startTask() {
        // Create a Runnable
        Runnable task = new Runnable() {
            public void run() {
                runTask();
            }
        };

        // Run the task in a background thread
        Thread backgroundThread = new Thread(task);
        // Terminate the running thread if the application exits
        backgroundThread.setDaemon(true);
        // Start the thread
        backgroundThread.start();
    }

    public void runTask() {
        for (int i = 1; i <= 10; i++) {
            try {
                board.add(pikachu, 5, 5);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void setPikachu(int r, int c) {
        board.add(pikachu, r, c);
        GridPane.setHalignment(pikachu, HPos.CENTER);
    }

}

1 个答案:

答案 0 :(得分:0)

  

我了解到GridPane不是线程安全的。

对于JavaFX场景节点,没有线程安全之类的东西。只能从JavaFX Application Thread修改它们。

  

我也尝试过使用Platform.runLater,但仍然无法正常工作。

您也必须将其放置在正确的位置。如果您做了Platform.runLater(this::startTask);,那么它什么也不会做。

您应该使用Platform.runLater(() -> board.....)来从非JavaFX Application Thread修改场景图。

最后,您实际上不应该使用board.add()。多次添加控件将抛出IllegalArgumentException,表示孩子有重复。

这就是您应该做的:

Platform.runLater(() -> board.setConstraints(pikachu, 5, 5));

我不确定为什么您总是将其设置为第5行和第5列。