我有一个带有图像的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);
}
}
答案 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列。