JavaFX UI 在事件监听器的 JavaFX 应用程序线程中卡住,但可与 Platform.runLater 一起使用

标签 java multithreading javafx

在我的 JavaFX 程序中,我当前的所有代码都在 JavaFX 应用程序线程上运行(包括不一定更新 UI 的游戏逻辑代码)。我读过,您应该在单独的后台线程中运行非 UI 代码,然后使用 Platform.runLaterTasks 在 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/

相关文章:

javafx - 为什么我的 JavaFx 应用程序在 RaspberryPi 上运行时没有框架?

java - 我想知道添加到 blob 存储的文件的位置

java - 测试用例无法正常工作。设置两个数字彼此相等的问题

java - 如何以更干净的方式使用递归在沙漏下面进行编码?

c# - 多核处理器上的多线程奇怪的结果

java - 方向改变时如何处理服务

java - 如何在使用 JavaFX 元素的 Travis 上运行测试?

java - 如何向 JavaFX 中除一个选项卡之外的所有选项卡添加关闭按钮?

java - 替换 Arraylist<HashMap> 位置

javascript - Angular CLI 中的网络 worker