JavaFX UI在事件侦听器的JavaFX应用程序线程中冻结,但可与Platform.runLater

时间:2018-11-07 07:42:47

标签: java multithreading javafx

在我的JavaFX程序中,我目前所有代码都在JavaFX Application Thread上运行(包括不一定更新UI的游戏逻辑代码)。我已经读过您应该在单独的后台线程中运行非UI代码,然后使用Platform.runLaterTasks在JavaFX Application Thread中相应地对其进行更新,以便不会阻塞UI,但是我对于我为什么只在JavaFX Application Thread上调用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,其中包含一堆按钮,而我正在收听的ObservableList中包含一堆瓦片。当此图块列表更改时,我想相应地更新FlowPane,以便所有新图块都在FlowPane中创建一个按钮。同样,当从图块列表中删除图块时,我希望将包含该图块的按钮从FlowPane中删除。

因此,根据我在网上发现的内容,事件侦听器在JavaFX Application Thread上运行。这意味着上面的代码正在JavaFX Application线程上运行。当我将按钮添加到FlowPane时,UI不会冻结/被阻止fpHand.getChildren().add()。另一方面,当我从FlowPane删除按钮时,UI冻结在fpHand.getChildren.remove上。

但是,当我用fpHand.getChildren.remove()包装Platform.runLater()调用时,UI会响应并且不会冻结。为什么是这样?当前所有代码都在JavaFX Application Thread上运行。

另外,我的代码的另一部分也有类似的问题。我有一个带有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(),则用户界面不会完全冻结,但是我之前在该位置添加的任何图块都好像桌子上有一个看不见的图块,因此我无法添加一块,尽管它应该是空的。 updateTable()函数也在JavaFX Application Thread和事件侦听器中被调用。如果我删除hbox.getChildren.clear(),似乎Platform.runLater()的调用不正确。

1 个答案:

答案 0 :(得分:0)

我遇到的问题是我正在使用show all files作为根窗格,该窗格中包含用于单击和拖动的图块的按钮,而没有消耗鼠标事件。

setMouseTransparent(true)

当您释放鼠标时,它会将其设置回false

    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();
    });

这里的问题是,当我从场景中删除按钮时,它会破坏所有事件处理程序,即使我释放鼠标,它也不会运行此代码。