在我的Java程序中,我给用户一些选项,其中之一调用JavaFXProgram来显示某些内容。当被调用的JavaFX实际退出时,我只想在Java程序中运行更多代码,可能需要5秒钟,也可能需要1分钟。理想情况下,我想要的是类似于Android的设备。我们先致电startActivityForResult()
,然后等待的致电onActivityResult()
。我如何在自己的情况下达到类似的行为?
我写了这段代码来尝试复制遇到的问题。这是类似的想法,但是以某种方式将其称为JavaFX,开始循环并毫无问题地从用户检索输入。在我的其他程序中,我总是得到Exception in thread "main" java.util.InputMismatchException
何时再次返回以扫描输入。但是正如我所说,理想情况下,我只想在JavaFX Application关闭后运行更多代码。
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); } }
编辑1:
我只是注意到,如果我尝试显示两次(例如:选择1,关闭JavaFX应用程序,然后再次选择1),它将崩溃Exception in thread "main" java.lang.IllegalStateException: Application launch must not be called more than once
。似乎该代码中的JavaFX Application也未正确退出。
您的代码无法正常使用,因为它不适合JavaFX Application生命周期,有关JavaFX Application的生命周期已在的API文档中进行了全面记录Application
。简而言之,Application
该类表示整个应用程序(或应用程序的生命周期)。
要在JavaFX中显示窗口,您必须在FX应用程序线程上执行此操作,并且必须启动FX工具包才能启动该线程(除其他外)。的Application.launch()
方法启动FX工具包,启动FX应用程序线程,创建您的应用程序类的init()
实例,start()
在该实例上调用,然后在该实例上调用(start()
对FX Application Thread 的调用)。
如 记载,Application.launch()
块(不返回),直到FX工具关闭(即退出应用程序),并且必须一次调用。(因为它代表了整个应用程序,所以这很有意义,并且没有办法解决两次调用它的问题。)
从用户的角度来看,您的应用程序结构也没有任何意义。为什么要让您的用户与命令行交互以向基于GUI的应用程序显示选项?您应该在GUI中显示这些选项。只需在启动时显示一个带有选项的窗口,然后显示与所选选项相对应的窗口。如果要确保用户在选择的选项完成之前无法返回到原始窗口,只需将新窗口设置为模态。
例如,如果您重构MyJavaFXProgram
它不是Application
子类(应该这样做,因为它不是应用程序的起点),则应该这样做:
import javafx.scene.Parent; import javafx.scene.layout.StackPane; import javafx.scene.text.Text; public class MyJavaFXProgram { private StackPane view ; public MyJavaFXProgram() { Text oText = new Text("My JavaFXProgram"); view = new StackPane(); view.getChildren().add(oText); } public Parent getView() { return view ; } }
那你就可以
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
。可以通过创建来启动FX工具包,JFXPanel
这确实有点麻烦,但是我更喜欢通过使用来明确地进行操作Application
实际上不做任何事情的类,调用launch()
并等待其初始化类来进行操作。完成。我在回答这个问题时展示了如何做到这一点,所以我将从那里借用该解决方案。这是Application
用于启动FX工具包的类(但不是您执行的主类):
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 } }
这个想法是调用Application.launch(FXStarter.class)
,但是由于launch()
阻塞,您需要在后台线程上执行该操作。由于这意味着您的后续代码可能会(可能会)在launch()
实际完成您需要完成的工作之前执行,因此您需要等待直到它完成工作为止,您可以使用来完成FXStarter.awaitFXToolkit()
。然后,您可以执行循环。剩下唯一需要担心的就是确保您在FX Application Thread上创建并显示新窗口。所以您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: FutureTaskshowProgramTask = 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(); } }
(使用与MyJavaFXProgram
上述相同的版本。)