在我的 JavaFX 程序中,我当前的所有代码都在 JavaFX 应用程序线程上运行(包括不一定更新 UI 的游戏逻辑代码)。我读过,您应该在单独的后台线程中运行非 UI 代码,然后使用 Platform.runLater
或 Tasks
在 JavaFX 应用程序线程中相应地更新它,以便 UI 不会被阻塞,但我仍然很困惑为什么当我在 JavaFX 应用程序线程上简单地调用 myFlowPane.getChildren().remove()
时 UI 会卡住。
以下代码有望提供足够的上下文
hand.getTiles().addListener((ListChangeListener<ObservableTile>) change -> {
while (change.next()) {
if (change.wasAdded()) {
for (Tile t : change.getAddedSubList()) {
var btn = tileButtonFactory.newTileButton(t);
fpHand.getChildren().add(change.getFrom(), btn);
}
} else if (change.wasRemoved()) {
// Player can only remove 1 tile at a time
Tile tile = change.getRemoved().get(0);
fpHand.getChildren().removeIf(node -> ((TileButton)node).getTile().equals(tile));
}
}
});
我有一个包含一堆按钮的 FlowPane,并且我正在监听一个包含一堆图 block 的 ObservableList
。当此图 block 列表更改时,我想相应地更新 FlowPane,以便任何新图 block 都在 FlowPane 中创建一个按钮。同样,当从该图 block 列表中删除某个图 block 时,我希望从 FlowPane 中删除包含该图 block 的按钮。
因此,根据我在网上找到的信息,事件监听器在 JavaFX 应用程序线程上运行。这意味着上面的代码正在 JavaFX 应用程序线程上运行。当我向 FlowPane 添加按钮时,UI不会卡住/被阻止fpHand.getChildren().add()
。另一方面,当我从 FlowPane 中删除按钮时,UI 在 fpHand.getChildren.remove
上卡住。
但是,当我使用 Platform.runLater()
封装 fpHand.getChildren.remove()
调用时,UI 具有响应能力并且不会卡住。为什么是这样?所有代码当前都在 JavaFX 应用程序线程上运行。
此外,我的代码的另一部分也有类似的问题。我有一个包含 128 个单元格的 GridPane(每个单元格都有一个 HBox),我需要更新表格。我有以下代码:
private void updateTable() {
Platform.runLater(() -> {
clearTable();
table.forEach(this::addTilesToTable);
});
}
private void clearTable() {
for (int row = 0; row < gpTable.getRowCount(); row++) {
for (int col = 0; col < gpTable.getColumnCount(); col++) {
getCellFromGridPane(row, col).ifPresent(hbox -> hbox.getChildren().clear());
}
}
}
如果我删除 Platform.runLater()
,UI 不会完全卡住,但我之前在该位置添加的任何图 block ,就好像 table 上有一个不可见的图 block ,因此我无法在那里添加图 block ,即使它应该是空的。 updateTable()
函数也在 JavaFX 应用程序线程和事件监听器中调用。如果我删除 Platform.runLater()
,似乎 hbox.getChildren.clear()
没有被正确调用。
最佳答案
我遇到的问题是,我在根 Pane 中使用了 setMouseTransparent(true)
,该 Pane 包含包含用于单击和拖动的图 block 的按钮,并且不消耗鼠标事件。
node.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {
root.setMouseTransparent(true);
dragContext.setMouseX(e.getSceneX());
dragContext.setMouseY(e.getSceneY());
dragContext.setInitialTranslateX(node.getTranslateX());
dragContext.setInitialTranslateY(node.getTranslateY());
e.consume();
});
当你释放鼠标时,它会将其设置回 false
node.addEventHandler(MouseEvent.MOUSE_RELEASED, e -> {
root.setMouseTransparent(false);
node.setTranslateX(0);
node.setTranslateY(0);
e.consume();
});
这里的问题是,当我从场景中删除按钮时,它会销毁所有事件处理程序,即使我释放鼠标,它也不会运行此代码。
关于JavaFX UI 在事件监听器的 JavaFX 应用程序线程中卡住,但可与 Platform.runLater 一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53185267/