2017-01-21 7 views
0

Java 프로그램에서 사용자에게 몇 가지 옵션을 제공하고 그 중 하나는 JavaFX 프로그램을 호출하여 뭔가를 표시합니다. 이 JavaFX가 실제로 종료 될 때 자바 프로그램에서 더 많은 코드를 실행하기 만하면됩니다. 5 초가 걸릴 수 있습니다. 이상적으로 내가 원하는 것은 Android에서와 같은 것입니다. startActivityForResult()으로 전화 한 다음 onActivityResult()의 전화를 기다립니다. 내 상황에서 어떻게 비슷한 행동을 할 수 있습니까?Java 프로그램에서 JavaFX를 호출하고 더 많은 코드를 실행하기 전에 잠시 기다려야합니다.

나는 내가 가지고있는 문제를 복제하려고 시도한이 코드를 가지고있다. 비슷한 생각이지만, 어떻게 든이 함수는 JavaFX를 호출하고, 루프를 시작하며 문제없이 사용자로부터 입력을 검색합니다. 내 다른 프로그램에서 나는 항상 Exception in thread "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 : 난 그냥 내가 뭔가를 두 번 표시하려고하면 눈치

: 그것은 Exception in thread "main" java.lang.IllegalStateException: Application launch must not be called more than once와 충돌 (예 : 1, 가까운 자바 FX 응용 프로그램을 선택 후 다시 1을 선택합니다). JavaFX 응용 프로그램이이 코드에서도 제대로 종료되지 않는 것으로 보입니다.

+0

당신이'showAndWait()'을 찾고있는이 방법이 아닙니다 : 같은 당신의 MyJavaProgram 지금 보이는? –

+0

아직 처리하지 않았습니까? ['Application.launch()'] (http://docs.oracle.com/javase/8/javafx/api/javafx/application/Application.html#launch-java.lang.Class-java.lang.String. ..-)는 JavaFX 응용 프로그램이 종료 될 때까지 반환되지 않습니다. 또한 일반적으로이 방법은'launch() '를 두 번 이상 호출 할 수 없으므로 작동하지 않습니다. –

+0

어쨌든 GUI 응용 프로그램을 명령 줄에서 구동하려는 이유는 무엇입니까? 이 작업을 수행하는 것은 매우 까다로운 작업입니다. 스레딩 등으로 손을 더러워해야합니다. 간단한 창에서 "나에게 뭔가를 보여줄 수있는"옵션을 표시하지 않는 것이 어떻습니까? –

답변

2

코드가 Application에 대한 API 설명서에 완전히 설명되어있는 JavaFX 응용 프로그램 수명주기에 맞지 않기 때문에 코드가 제대로 작동하지 않습니다. 간단히 말해서 Application 클래스는 전체 응용 프로그램 (또는 응용 프로그램의 수명주기)을 나타냅니다.

JavaFX에서 창을 표시하려면 FX 응용 프로그램 스레드에서 수행해야하며 FX 도구 키트를 시작해야이 스레드를 시작할 수 있습니다. Application.launch() 메서드는 FX 도구 키트를 시작하고 FX 응용 프로그램 스레드를 시작하고 응용 프로그램 클래스의 인스턴스를 만들고 해당 인스턴스에서 init()을 호출 한 다음 해당 인스턴스에서 start()을 호출합니다 (start()에 대한 호출은 FX 응용 프로그램 스레드에서 발생 함).

documented으로, Application.launch()은 FX 툴킷이 종료 될 때까지 (즉, 응용 프로그램이 종료 될 때까지) 차단 (반환되지 않음)하므로 한 번만 호출해야합니다. (전체 응용 프로그램을 나타내므로 의미가 있으며 두 번 호출하는 방법을 사용할 수 없습니다.)

응용 프로그램 구조가 실제로 사용자 관점에서도 이해가되지 않습니다. GUI 기반 응용 프로그램에 옵션을 표시하기 위해 사용자에게 명령 행을 사용하도록 요청하는 이유는 무엇입니까? 이러한 옵션을 GUI에 표시해야합니다. 시작할 때 옵션이있는 창을 표시 한 다음 선택한 옵션에 해당하는 창을 표시하십시오. 선택한 옵션이 완료되기 전에 사용자가 원래 창으로 돌아갈 수 없도록하려면 새 창을 모달로 만드십시오. 예를 들어

, 당신은 (이 응용 프로그램의 출발점이되지 않기 때문에 당신이해야) 그래서이 Application 서브 클래스가 아닌 MyJavaFXProgram를 리팩토링하는 경우 :

다음
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의 하위 클래스가 아니어야합니다. JFXPanel을 생성하여 FX 툴킷을 시작하기위한 해킹이 있습니다. 그러나 실제로는 해킹이 약간 있습니다. Application 클래스를 사용하여 실제로 수행하지 않는 클래스 인 launch()을 기다리고 기다리고 있습니다. 초기화가 완료되었습니다. this question에 대한 답변에서이 작업을 수행하는 방법을 보여 주었으므로 여기에서 해당 솔루션을 빌려 올 것입니다. 여기에 FX 툴킷을 시작하는 데 사용되는 Application 클래스 (그러나 당신이 실행 메인 클래스가되지 않음)입니다 :

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 응용 프로그램 스레드에 새 창을 만들고 표시하는지 확인하는 것입니다.

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

} 

(이것은 위의 MyJavaFXProgram의 동일한 버전을 사용합니다.)