JavaFX Создание двух этапов одновременно

1

Я пытаюсь показать два этапа одновременно в JavaFX, где на первом этапе предполагается показать progessbar и который должен закрыться, как только будет готов второй этап. Я попробовал работать как через Platform.runLater, так и через Tasks, но проблема в том, что этапы оба замораживаются до тех пор, пока обе не будут завершены, а апробация проекта не будет анимирована до тех пор, пока второй этап не завершит загрузку.

Вот некоторые выдержки из кода:

public void start(Stage primaryStage) throws Exception {
        new Thread(progressTask()).start();
        new Thread(loginTask()).start();
    }



    public Task<Void> loginTask() {
        Task<Void> t = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                Platform.runLater(new Runnable() {

                    @Override
                    public void run() {

                        Stage s = new Stage();
                        FXMLLoader loader = new FXMLLoader(getClass().getResource("Login.fxml"));
                        Scene sc = null;
                        try {

                            sc = new Scene((Parent) loader.load());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                        s.setScene(sc);
                        s.show();

                    }

                });
                return null;
            };
        };
        return t;
    }

    public Task<Void> progressTask() {

        Task<Void> t = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                Platform.runLater(new Runnable() {

                    @Override
                    public void run() {
                            ProgressBar bar = new ProgressBar(0);
                            bar.setPrefSize(200, 24);
                            Timeline task = new Timeline(new KeyFrame(Duration.ZERO, new KeyValue(bar.progressProperty(), 0)), new KeyFrame(
                                    Duration.seconds(5), new KeyValue(bar.progressProperty(), 1)));

                            VBox layout = new VBox(10);
                            layout.getChildren().setAll(bar);
                            Stage stage = new Stage(StageStyle.DECORATED);
                            stage.setScene(new Scene(layout));
                            stage.initModality(Modality.WINDOW_MODAL);
                            stage.show();
                            new Thread() {
                                public void run() {
                                    task.playFromStart();
                                }
                            }.start();
                            task.setOnFinished(new EventHandler<ActionEvent>() {
                                    @Override
                                    public void handle(ActionEvent event) {
                                        stage.close();


                                    }

                                });
                    }
            });
                return null;
        }


};

Спасибо за вашу помощь.

Показать ещё 3 комментария
Теги:
javafx
user-interface
javafx-8

1 ответ

3
Лучший ответ

Вы должны создать Task только для выполнения длительной операции и использовать обратные вызовы для обновления пользовательского интерфейса. Редко бывает, что нужно вызвать Platform.runLater(...) самостоятельно.

В вашем коде Task не имеет никакой цели, потому что они просто планируют весь код для работы в потоке приложения FX с Platform.runLater(...). Таким образом, вы эффективно создаете новый поток, чтобы спросить, какой код будет выполняться в текущем потоке.

Вот пример, который делает то, что я думаю, что вы хотите сделать:

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class DataLoadingStageExample extends Application {

    @Override
    public void start(Stage primaryStage) {

        Scene scene = buildInitialUI();
        primaryStage.setScene(scene);

        Stage monitorStage = new Stage();

        Task<DataClass> dataLoadingTask = createDataLoadingTask();

        // update UI when dataLoadingTask finishes
        // this will run on the FX Application Thread
        dataLoadingTask.setOnSucceeded(event -> {
            DataClass data = dataLoadingTask.getValue();
            scene.setRoot(createUIFromData(data));
            monitorStage.hide();
        });

        buildProgressUI(monitorStage, dataLoadingTask);

        // manage stage layout:
        primaryStage.yProperty().addListener((obs, oldY, newY) -> monitorStage.setY(newY.doubleValue() - 100));
        primaryStage.setTitle("Application");

        // show both stages:
        monitorStage.show();
        primaryStage.show();

        // start data loading in a background thread:
        new Thread(dataLoadingTask).start();

    }

    private void buildProgressUI(Stage monitorStage,
            Task<?> task) {
        ProgressBar progressBar = new ProgressBar();
        progressBar.progressProperty().bind(task.progressProperty());

        Scene monitorScene = new Scene(new StackPane(progressBar), 400, 75);
        monitorStage.setScene(monitorScene);
        monitorStage.setTitle("Loading progress");
    }

    private Scene buildInitialUI() {
        Label loadingLabel = new Label("Loading...");
        StackPane root = new StackPane(loadingLabel);
        Scene scene = new Scene(root, 600, 400);
        return scene;
    }

    private Task<DataClass> createDataLoadingTask() {
        return new Task<DataClass>() {
            @Override
            public DataClass call() throws Exception {
                // mimic connecting to database
                for (int i=0; i < 100; i++) {
                    updateProgress(i+1, 100);
                    Thread.sleep(50);
                }
                return new DataClass("Data");
            }
        };
    }

    private Parent createUIFromData(DataClass data) {
        // obviously much more complex in real life
        Label label = new Label(data.getData());
        return new StackPane(label);
    }

    public static class DataClass {
        // obviously much more complex in real life
        private final String data ;
        public DataClass(String data) {
            this.data = data ;
        }
        public String getData() {
            return data ;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
  • 0
    Большое спасибо. Теперь работает :)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню