简体   繁体   English

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

[英]JavaFX UI Freezes in JavaFX Application Thread in Event Listener but works with Platform.runLater

In my JavaFX program, I currently have all my code running on the JavaFX Application Thread (including game logic code that does not necessarily update the UI). 在我的JavaFX程序中,当前所有代码都在JavaFX Application Thread上运行(包括不一定更新UI的游戏逻辑代码)。 I've read that you should run non-UI code in a separate background thread and then update it accordingly in the JavaFX Application Thread using Platform.runLater or Tasks , so that the UI is not blocked, but I'm still confused as to why the UI freezes when I simply call myFlowPane.getChildren().remove() on the JavaFX Application Thread. 我已经读过,您应该在单独的后台线程中运行非UI代码,然后使用Platform.runLaterTasks在JavaFX Application Thread中相应地对其进行更新,以使UI不会被阻塞,但是我仍然感到困惑为什么当我仅在JavaFX Application Thread上调用myFlowPane.getChildren().remove()时,UI会冻结?

The following code hopefully provides enough context 以下代码有望提供足够的上下文

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

I have a FlowPane that contains a bunch of buttons, and I'm listening on an ObservableList that contains a bunch of tiles. 我有一个FlowPane,其中包含一堆按钮,并且正在侦听包含一堆瓦片的ObservableList When this list of tiles changes, I want to update the FlowPane accordingly so that any new tiles, I create a button in the FlowPane. 当此图块列表更改时,我想相应地更新FlowPane,以便所有新图块都在FlowPane中创建一个按钮。 Similarly, when a tile is removed from this list of tiles, I want the button that contains this Tile to be removed from the FlowPane. 同样,当从此图块列表中删除图块时,我希望将包含该图块的按钮从FlowPane中删除。

So from what I found online, event listeners run on the JavaFX Application Thread. 因此,根据我在网上发现的内容,事件侦听器在JavaFX Application Thread上运行。 This means that the code above is running on the JavaFX Application thread. 这意味着上面的代码正在JavaFX Application线程上运行。 When I add a button to my FlowPane, the UI does not freeze/get blocked fpHand.getChildren().add() . 当我添加一个按钮,我FlowPane,该UI 不会冻结/得到阻止fpHand.getChildren().add() On the other hand, when I remove a button from the FlowPane, the UI freezes on fpHand.getChildren.remove . 另一方面,当我从FlowPane删除按钮时,UI冻结在fpHand.getChildren.remove

However, when I wrap the fpHand.getChildren.remove() call with a Platform.runLater() , the UI is responsive and doesn't freeze. 但是,当我使用Platform.runLater()包装fpHand.getChildren.remove()调用时,UI会响应并且不会冻结。 Why is this? 为什么是这样? All the code is currently running on the JavaFX Application Thread. 当前所有代码都在JavaFX Application Thread上运行。

Also, I have a similar problem with another part of my code. 另外,我的代码的另一部分也有类似的问题。 I have a GridPane with 128 cells (which each cell has an HBox) and I need to update the table. 我有一个带有128个单元格的GridPane(每个单元格都有一个HBox),我需要更新表。 I have the following code: 我有以下代码:

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

If I remove the Platform.runLater() , the UI doesn't freeze completely, but any tiles I've added previously in the spot, it's as if there's an invisible tile there on the table, and hence I cannot add a tile there, even though it should be empty. 如果我删除Platform.runLater() ,则UI不会完全冻结,但是我之前在该位置添加的任何图块,就好像桌子上有一个看不见的图块,因此我无法在其中添加图块,即使它应该为空。 The updateTable() function is also called in the JavaFX Application Thread, and in an event listener. 在JavaFX Application Thread和事件侦听器中也调用了updateTable()函数。 It seems as if the hbox.getChildren.clear() isn't being called properly if I remove Platform.runLater() . 如果我删除Platform.runLater() ,似乎没有正确调用hbox.getChildren.clear() Platform.runLater()

The problem I had was that I was using setMouseTransparent(true) for the root panes that hold the buttons containing the Tile for click and drag and not consume mouse events. 我遇到的问题是,我正在对包含用于单击和拖动的Tile的按钮的根窗格使用setMouseTransparent(true) ,而不消耗鼠标事件。

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

And when you release the mouse, it sets it back to false 当您释放鼠标时,它将其设置回false

    node.addEventHandler(MouseEvent.MOUSE_RELEASED, e -> {
        root.setMouseTransparent(false);
        node.setTranslateX(0);
        node.setTranslateY(0);
        e.consume();
    });

The problem here was that when I remove the button from the scene, it destroys all event handlers, and even though i release the mouse, it doesn't run this code. 这里的问题是,当我从场景中删除按钮时,它会破坏所有事件处理程序,并且即使我释放鼠标,它也不会运行此代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM