在Java程序中调用JavaFX并等待等待退出,然后再运行更多代码

在Java程序中调用JavaFX并等待等待退出,然后再运行更多代码,java,javafx,wait,onactivityresult,startactivityforresult,Java,Javafx,Wait,Onactivityresult,Startactivityforresult,在我的java程序中,我给用户一些选项,其中一个调用JavaFX程序来显示一些内容。我只想在Java程序中运行更多的代码,当调用的JavaFX实际退出时,可能需要5秒,可能需要一分钟。理想情况下,我想要的是类似于Android的东西。我们调用startActivityForResult(),然后等待调用onActivityResult()。 在我的情况下,我如何才能实现类似的行为 我编写了这段代码,试图复制我遇到的问题。这是一个类似的想法,但不知何故,它调用了JavaFX,进入循环的开始,并从用

在我的java程序中,我给用户一些选项,其中一个调用JavaFX程序来显示一些内容。我只想在Java程序中运行更多的代码,当调用的JavaFX实际退出时,可能需要5秒,可能需要一分钟。理想情况下,我想要的是类似于Android的东西。我们调用
startActivityForResult()
,然后等待调用
onActivityResult()
。 在我的情况下,我如何才能实现类似的行为

我编写了这段代码,试图复制我遇到的问题。这是一个类似的想法,但不知何故,它调用了JavaFX,进入循环的开始,并从用户那里毫无问题地检索输入。在我的另一个程序中,当线程“main”java.util.InputMismatchException返回扫描输入时,我总是在线程“main”java.util.InputMismatchException中得到
异常。但正如我所说,理想情况下,我只希望在JavaFX应用程序关闭后运行更多代码

package JavaCallsJavaFXandWaits;

import java.util.Scanner;
import javafx.application.Application;

public class MyJavaProgram {
    public static void main(String[] args) {
        int input;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("0 - exit");
            System.out.println("1 - display something to me");
            input = scanner.nextInt();
            switch (input) {
                case 0:
                    break;
                case 1:
                    Application.launch(JavaCallsJavaFXandWaits.MyJavaFXProgram.class, null);
                    // how to get notified of MyJavaFXProgram exit? I only want to run code after it exits
                    break;

            }
            if (input == 0) break;
        }


    }

}

package JavaCallsJavaFXandWaits;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class MyJavaFXProgram extends Application {

    @Override
    public void start(Stage primaryStage) {
        Text oText = new Text("My JavaFXProgram");

        StackPane root = new StackPane();
        root.getChildren().add(oText);

        Scene scene = new Scene(root, 800, 600);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

}
edit1:


我刚刚注意到,如果我尝试两次显示某个内容(例如:选择1,关闭JavaFX应用程序,然后再次选择1),它会在线程“main”java.lang.IllegalStateException中出现
异常崩溃:不能多次调用应用程序启动
。在这段代码中,JavaFX应用程序似乎也没有正确退出

您的代码无法正常工作,因为它不适合JavaFX应用程序的生命周期,而JavaFX应用程序的生命周期在的API文档中有完整的文档记录。简而言之,
Application
类代表整个应用程序(或者可能是应用程序的生命周期)

要在JavaFX中显示窗口,必须在FX应用程序线程上执行此操作,并且必须启动FX工具箱才能启动此线程(除其他外)。
Application.launch()
方法启动FX工具箱,启动FX应用程序线程,创建应用程序类的实例,对该实例调用
init()
,然后对该实例调用
start()
(对
start()
的调用发生在FX应用程序线程上)

作为,
Application.launch()
将阻塞(不返回),直到FX toolkit关闭(即应用程序退出),并且只能调用一次。(因为它代表整个应用程序,所以这是有意义的,没有办法避免调用它两次。)

从用户的角度来看,您的应用程序结构也没有任何意义。为什么要求用户与命令行交互以向基于GUI的应用程序提供选项?您应该在GUI中显示这些选项。只需在启动时显示包含选项的窗口,然后显示与所选选项对应的窗口。如果要确保用户在所选选项完成之前无法返回到原始窗口,只需将新窗口设置为模态

例如,如果您重构
MyJavaFXProgram
,使其不是
应用程序
子类(您应该这样做,因为它不是应用程序的起点):

那你就可以了

import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class MyJavaProgram extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Button showMeSomethingButton = new Button("Show me something");
        showMeSomethingButton.setOnAction(e -> {
            MyJavaFXProgram myProgram = new MyJavaFXProgram();
            showInModalWindow(myProgram.getView());
        });
        Button exitButton = new Button("Exit");
        exitButton.setOnAction(e -> Platform.exit());

        VBox root = new VBox(10, exitButton, showMeSomethingButton);
        root.setPadding(new Insets(20));
        root.setAlignment(Pos.CENTER);
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void showInModalWindow(Parent view) {
        Stage stage = new Stage();
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.setScene(new Scene(view));
        stage.show();
    }


}
如果您真的想从命令行驱动所有这一切(老实说,我看不出这样做的正当理由),那么它会变得很棘手,您必须在某个时候管理线程之间的交互。最简单的方法可能是确保FX工具箱在应用程序启动时启动,然后或多或少地按照代码中的方式进行操作,但要再次重构
MyJavaFXProgram
,使其不是
application
的子类。通过创建
JFXPanel
,启动FXToolkit是一种黑客行为,但这确实是一种黑客行为,我更愿意通过创建一个实际上什么都不做的
应用程序
类,调用
launch()
,并等待初始化完成来显式启动FXToolkit。我在对的回答中展示了如何做到这一点,所以我将从那里借用这个解决方案。以下是用于启动FX toolkit的
应用程序
类(但不是您执行的主类):

我们的想法是调用
Application.launch(FXStarter.class)
,但是由于
launch()
阻塞,您需要在后台线程上执行此操作。因为这意味着后续代码可能(可能会)在
launch()
实际完成您需要它完成的工作之前执行,所以您需要等待它完成它的工作,您可以使用
FXStarter.awaitFXToolkit()
。然后可以执行循环。唯一需要担心的是确保在FX应用程序线程上创建并显示新窗口。因此,您的
MyJavaProgram
现在看起来像:

import java.util.Scanner;
import java.util.concurrent.FutureTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class MyJavaProgram {
    public static void main(String[] args) throws Exception {

        // start FX toolkit on background thread:
        new Thread(() -> Application.launch(FXStarter.class)).start();
        // wait for toolkit to start:
        FXStarter.awaitFXToolkit();

        // make sure closing first window does not exit FX toolkit:
        Platform.setImplicitExit(false);

        int input;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("0 - exit");
            System.out.println("1 - display something to me");
            input = scanner.nextInt();
            switch (input) {
                case 0:
                    break;
                case 1:
                    // task to show UI:
                    FutureTask<Void> showProgramTask = new FutureTask<>(() -> {
                        MyJavaFXProgram program = new MyJavaFXProgram();
                        Stage stage = new Stage();
                        stage.setScene(new Scene(program.getView(), 400, 400));
                        stage.setOnShown(e -> {
                            stage.toFront();
                            stage.requestFocus();
                        });
                        // showAndWait will block execution until window is hidden:
                        stage.showAndWait();
                        return null ;
                    });
                    // show UI on FX Application Thread:
                    Platform.runLater(showProgramTask);
                    // block until task completes (i.e. window is hidden):
                    showProgramTask.get() ;
                    break;

            }
            if (input == 0) break;
        }

        // all done, exit FX toolkit:
        Platform.exit();
        scanner.close();
    }

}
import java.util.Scanner;
导入java.util.concurrent.FutureTask;
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.scene.scene;
导入javafx.stage.stage;
公共类MyJavaProgram{
公共静态void main(字符串[]args)引发异常{
//在后台线程上启动FX toolkit:
新线程(()->Application.launch(FXStarter.class)).start();
//等待工具箱启动:
FXStarter.awaitFXToolkit();
//确保关闭第一个窗口不会退出FX toolkit:
Platform.setImplicitExit(false);
int输入;
扫描仪=新的扫描仪(System.in);
while(true){
System.out.println(“0-退出”);
System.out.println(“1-向我显示某物”);
输入=scanner.nextInt();
开关(输入){
案例0:
打破
案例1:
//要显示UI的任务:
FutureTask showProgramTask=新的FutureTask(()->{
MyJavaFXProgram=新的MyJavaFXProgram();
阶段=新阶段();
import java.util.concurrent.CountDownLatch;

import javafx.application.Application;
import javafx.stage.Stage;

public class FXStarter extends Application {

    private static final CountDownLatch latch = new CountDownLatch(1);

    public static void awaitFXToolkit() throws InterruptedException {
       latch.await();
    }

    @Override
    public void init() {
        latch.countDown();
    }

    @Override
    public void start(Stage primaryStage) {
        // no-op
    }
}
import java.util.Scanner;
import java.util.concurrent.FutureTask;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class MyJavaProgram {
    public static void main(String[] args) throws Exception {

        // start FX toolkit on background thread:
        new Thread(() -> Application.launch(FXStarter.class)).start();
        // wait for toolkit to start:
        FXStarter.awaitFXToolkit();

        // make sure closing first window does not exit FX toolkit:
        Platform.setImplicitExit(false);

        int input;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println("0 - exit");
            System.out.println("1 - display something to me");
            input = scanner.nextInt();
            switch (input) {
                case 0:
                    break;
                case 1:
                    // task to show UI:
                    FutureTask<Void> showProgramTask = new FutureTask<>(() -> {
                        MyJavaFXProgram program = new MyJavaFXProgram();
                        Stage stage = new Stage();
                        stage.setScene(new Scene(program.getView(), 400, 400));
                        stage.setOnShown(e -> {
                            stage.toFront();
                            stage.requestFocus();
                        });
                        // showAndWait will block execution until window is hidden:
                        stage.showAndWait();
                        return null ;
                    });
                    // show UI on FX Application Thread:
                    Platform.runLater(showProgramTask);
                    // block until task completes (i.e. window is hidden):
                    showProgramTask.get() ;
                    break;

            }
            if (input == 0) break;
        }

        // all done, exit FX toolkit:
        Platform.exit();
        scanner.close();
    }

}