JavaFX同时创建两个阶段

时间:2014-11-26 13:24:40

标签: java user-interface javafx javafx-8

我试图在JavaFX中显示两个同时的阶段,其中第一阶段应该显示一个progessbar,并且一旦第二阶段准备好显示就应该关闭。 我尝试通过 Platform.runLater 任务运行,但问题是这两个阶段都被冻结,直到两个完成加载并且progessbar开始动画直到第二个舞台完成加载。

这里有一些代码摘录:

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


};

感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

您应该创建Task只是为了执行长时间运行的操作,并使用回调来更新UI。很少有人需要自己致电Platform.runLater(...)

在您的代码中,Task完全不起作用,因为它们只是安排所有代码在Platform.runLater(...)的FX应用程序线程上运行。因此,有效地创建一个新线程只是为了让一些代码在当前线程上执行。

这是一个我认为你想要做的事情的例子:

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