Основной поток приложения не обновляет графический интерфейс, пока не достигнет конца метода

#java #javafx

#java #javafx

Вопрос:

У меня есть кнопка подключения в приложении и две панели, размещенные на панели стека. Первая панель — chatPane, а вторая панель — загрузчик с .gif, установленным на его фоне.

Что я хочу сделать, так это после нажатия кнопки подключиться, я хочу отобразить загрузчик (с движущимся gif на его фоне), затем дождаться завершения метода delegate.ConnectToServer(..), а после этого выключить панель загрузчика и переключиться обратно на chatPane.

С текущим кодом кажется, что поток приложения JavaFX не визуализирует изменения, внесенные в графический интерфейс, пока не достигнет конца метода connectClicked(), что означает, что панель загрузчика не отображается.

Я попытался запустить содержимое метода setConnectionStatus в качестве задачи в потоке, но при этом я получил Not On fx application thread исключение методом setText объекта Label, однако загрузчик gif отображался красиво.

Я также пытался использовать переход паузы следующим образом:

 @FXML
public void connectClicked() {
    setConnectionStatus(CONNECTING);
    PauseTransition pause = new PauseTransition(Duration.seconds(0.1));

    pause.setOnFinished(event -> {
        try {
            delegate.connectToServer(connectionConfig.getServerAddress().getIp(),
                        connectionConfig.getServerAddress().getPort(),
                        connectionConfig.getTimeout());
            setConnectionStatus(CONNECTED);
            } catch (ConnectionException e) {
                setConnectionStatus(DISCONNECTED);
                //todo reconnect dialog
            } catch (FatalException e) {
                handleFatalError("Connection error", e.getMessage());
            }
        });
        pause.play();
    }
  

Это привело к тому, что панель загрузчика отображалась движущейся в течение 0,1 с, и после этого анимация остановилась (панель загрузчика осталась видимой, только gif остановился)

Я также попытался обернуть блок try catch с Platform.runLater() помощью or Thread , но это не имело никакого эффекта.

Я не могу использовать Platform.runLater для отображения загрузчика, потому что я хочу, чтобы изменения в графическом интерфейсе выполнялись немедленно, а не «когда-нибудь в будущем»

Вот полный код:

 @FXML public VBox loader;
@FXML public SplitPane chatPane;
@FXML public MenuItem connectMenuItem;
@FXML public MenuItem disconnectMenuItem;
@FXML public Label connectionStatus;

@FXML
public void connectClicked() {
       setConnectionStatus(CONNECTING);
        try {
            delegate.connectToServer(connectionConfig.getServerAddress().getIp(),
                    connectionConfig.getServerAddress().getPort(),
                    connectionConfig.getTimeout());
            setConnectionStatus(CONNECTED);

        } catch (ConnectionException e) {
            setConnectionStatus(DISCONNECTED);
            //todo reconnect dialog
        } catch (FatalException e) {
            handleFatalError("Connection error", e.getMessage());
        }
    }

     private void setConnectionStatus(ConnectionStatus status) {
        switch (status) {
            case CONNECTED:
                setStatusLabel("Connected", GREEN);
                setConnectionMenu(false, true);
                showChatPane();
                break;
            case DISCONNECTED:
                setStatusLabel("Not Connected", RED);
                setConnectionMenu(true, false);
                showChatPane();
                break;
            case CONNECTING:
                setStatusLabel("Connecting...", BLUE);
                setConnectionMenu(false, false);
                showLoader();
                break;
            case DISCONNECTING:
                setStatusLabel("Disconnecting...", BLUE);
                setConnectionMenu(false, false);
                showLoader();
                break;
        }
    }

    private void setStatusLabel(String text, Color color) {
        connectionStatus.setText(text);
        connectionStatus.setTextFill(color);
    }

    private void setConnectionMenu(boolean isConnectEnabled, boolean isDisconnectEnabled) {
        connectMenuItem.setDisable(!isConnectEnabled);
        disconnectMenuItem.setDisable(!isDisconnectEnabled);
    }

    private void showLoader() {
        chatPane.setVisible(false);
        loader.setVisible(true);
    }

    private void showChatPane() {
        chatPane.setVisible(true);
        loader.setVisible(false);
    }
  

Комментарии:

1. Поместите свою логику в поток, отличный от потока пользовательского интерфейса.

2. Обертывание блока try catch потоком не имело никаких эффектов, панель загрузчика по-прежнему не отображается

3. Google для «фоновой задачи JavaFX», и я уверен, что вы найдете много ресурсов. Логика, не связанная с пользовательским интерфейсом (например, установление соединения, что занимает много времени), должна выполняться вне потока пользовательского интерфейса. Логика пользовательского интерфейса (например, обновление метки) должна выполняться в потоке пользовательского интерфейса.

4. Включение логики в поток сработало, это была просто моя глупость, потому что я использовал thread.run() вместо thread.start()

Ответ №1:

Похоже, что обертывание блока try catch с помощью потока сработало, раньше это не работало, потому что я использовал thread.run() вместо thread.start() …

Также мне пришлось добавить Platform.runLater в setConnectionStatus методы внутри тела потока.