2017-01-21 2 views
0

В моей программе java я даю некоторые параметры пользователю, и один из них вызывает JavaFXProgram для отображения чего-либо. Я только хочу запустить больше кода в программе Java, когда этот JavaFX, который получил вызванное фактически, выходит, это может занять 5 секунд, это может занять минуту. В идеале, что бы я хотел, это что-то вроде Android. Мы звоним startActivityForResult(), а затем дождитесь звонка onActivityResult(). Как я могу добиться подобного поведения в моей ситуации?Вызовите JavaFX в программе Java и дождитесь ожидания до выхода до запуска большего количества кода

У меня есть этот код, который я написал, чтобы попытаться воспроизвести проблему, которая у меня есть. Это аналогичная идея, но почему-то это вызывает 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:

Я просто заметил, что если я пытаюсь показать что-то в два раза (например, выбрать 1, закройте приложение JavaFX, затем выберите 1 раз) он падает с Exception in thread "main" java.lang.IllegalStateException: Application launch must not be called more than once. Похоже, что приложение JavaFX тоже не выходит из этого кода.

+0

Не является ли это метод, который вы ищете 'showAndWait()'? –

+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

Ваш код не работает так, как он есть у вас, так как он не соответствует жизненному циклу приложения JavaFX Application, который полностью документирован в документации API для Application. Короче говоря, класс Application представляет полное приложение (или, возможно, жизненный цикл приложения).

Чтобы показать окно в JavaFX, вы должны сделать это в потоке приложения FX, и необходимо запустить инструментарий FX, чтобы начать эту тему (между прочим). Метод Application.launch() запускает инструментарий FX, запускает поток приложений FX, создает экземпляр вашего класса приложения, вызывает в этом экземпляре init(), а затем вызывает на этом экземпляре start() (вызов start() происходит в потоке приложения FX).

Как documented, Application.launch() блоки (не возвращаются) до тех пор, пока инструментарий FX не выключится (то есть приложение не выйдет) и должно быть вызвано только один раз. (Так как он представляет все приложение, это имеет смысл, и нет возможности обойти его дважды.)

Ваша структура приложения также не имеет никакого смысла с точки зрения пользователя. Зачем запрашивать у пользователя взаимодействие с командной строкой, чтобы представить параметры приложения на основе графического интерфейса? Вы должны представить эти параметры в графическом интерфейсе. Просто покажите окно с параметрами при запуске, а затем покажите окно, соответствующее выбранному параметру. Если вы хотите, чтобы пользователь не смог вернуться в исходное окно до того, как выбранный вариант будет завершен, просто сделайте новое окно модальным.

Например, если вы реорганизовывать 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, создав , но это действительно немного взломанный, и я предпочитаю делать это явно, имея класс Application, который на самом деле ничего не делает, вызывая launch() и ожидая его инициализация завершена. Я показал, как это сделать в ответ на this question, поэтому я просто займу это решение оттуда. Вот 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. Так что ваш 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(); 
    } 

} 

(Это использует ту же версию MyJavaFXProgram выше.)

Смежные вопросы