Запуск JavaFX из основного метода класса, который не расширяет Application

У меня возникла проблема с запуском приложения JavaFX из основного метода класса, который не расширяет javafx.application.Application

В моем приложении есть MainApp.java, который должен запустить метод переопределения start() в MainUIController.java, который расширяет Applciation

Когда я запускаю метод Main из MainUIController.java, все работает нормально.

MainApp.java

public class MainApp {

    public static void main(String[] args) {
        PersonJDBCTemplate jdbc = connect();
        MainUIController mUIc = new MainUIController(jdbc);
        mUIc.start(new Stage());

    }

    public static PersonJDBCTemplate connect() {
        ApplicationContext context = new ClassPathXmlApplicationContext(
                "Beans.xml");
        PersonJDBCTemplate personJDBCTemplate = (PersonJDBCTemplate) context
                .getBean("personJDBCTemplate");
        return personJDBCTemplate;
    }
}

MainUIController.java

public class MainUIController extends Application {

    private Stage stage;
    // private User loggedUser;
    private final double MINIMUM_WINDOW_WIDTH = 800.0;
    private final double MINIMUM_WINDOW_HEIGHT = 570.0;
    private String version = "0.6";
    private PersonJDBCTemplate jdbc;

    public MainUIController(PersonJDBCTemplate jdbc) {
        this.jdbc = jdbc;

    }

    @Override
    public void start(Stage primaryStage) {
        try {
            stage = primaryStage;
            stage.setTitle("Sharp");
            stage.setMinWidth(MINIMUM_WINDOW_WIDTH);
            stage.setMinHeight(MINIMUM_WINDOW_HEIGHT);
            stage.setResizable(false);
            gotoLogin();
            primaryStage.show();
        } catch (Exception ex) {
            Logger.getLogger(MainUIController.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }

    public void gotoLogin() {
        try {
            LoginController login = (LoginController) replaceSceneContent("/fxml/Login.fxml");
            login.setApp(this);
        } catch (Exception ex) {
            Logger.getLogger(MainUIController.class.getName()).log(
                    Level.SEVERE, null, ex);
        }
    }
}

После запуска MainApp я получаю следующую ошибку:

Exception in thread "main" java.lang.ExceptionInInitializerError
at javafx.stage.Window.<init>(Window.java:1110)
at javafx.stage.Stage.<init>(Stage.java:236)
at javafx.stage.Stage.<init>(Stage.java:224)
at ch.kit.sharp.main.MainApp.main(MainApp.java:15)
Caused by: java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = main
at com.sun.glass.ui.Application.checkEventThread(Application.java:445)
at com.sun.glass.ui.Screen.setEventHandler(Screen.java:245)
at com.sun.javafx.tk.quantum.QuantumToolkit.setScreenConfigurationListener(QuantumToolkit.java:600)
at javafx.stage.Screen.<clinit>(Screen.java:80)
... 4 more

Ответ 1

В дополнение к тому, что сказал Nejinx, вы не должны напрямую называть свой start(), всегда вызывайте launch(), потому что он настраивает среду JavaFX, включая creation of stage и calls start(), передавая его в качестве параметра.

В документах есть примечание, в котором указано это

ПРИМЕЧАНИЕ. Этот метод вызывается в потоке приложений JavaFX

launch() можно вызывать из любого класса, принимая во внимание, если класс напрямую не распространяется javafx.application.Application, вы должны передать класс, расширяющий его как аргумент метода запуска.

Например, рассмотрим, что у вас есть класс JavaFXMain, который расширяет Application

class JavaFXMain extends Application {...}

Вы можете использовать любой другой класс, чтобы запустить приложение JavaFX.

class Main {
   ...
   public void someMethod() {
      ...
      JavaFXMain.launch(JavaFXMain.class); // Launch the JavaFX application
      ...
   }
}

В вашем случае вы можете попробовать что-то вроде этого в основном методе MainApp:

// You may remove args if you don't intend to pass any arguments
MainUIController.launch(MainUIController.class, args) 

Ответ 2

Вам нужно инициализировать среду JavaFX, вы не можете создать новый этап вне запуска (args); сначала вызываемый классом, который расширяет приложение.

Ответ 3

Это было очень полезно, но приложение FX стало самостоятельным приложением. Вы не можете передавать объекты из своего кода, отличного от FX, и вам не предоставляется дескриптор созданного экземпляра приложения.

Я придумал это обходное решение, что я не сумасшедший, но он позволяет передавать параметры.

package hacks;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

/**
 * Created by WorkDay on 8/11/16.<br>
 * <br>
 * HelloWorld is a javaFX app that needs parameters that are real objects
 */

class AppParameterLauncher {
    public static void main(String[] args) {
        HelloWorld.launch(new ObjectThatContainsData("brave"), new ObjectThatContainsData("new"));
    }
}


public class HelloWorld extends Application {

    private static ObjectThatContainsData staticData1 = null;
    private static ObjectThatContainsData staticData2 = null;


    public static void launch(ObjectThatContainsData data1, ObjectThatContainsData data2) {
        HelloWorld.staticData1 = data1;
        HelloWorld.staticData2 = data2;
        Application.launch(HelloWorld.class);
    }

    private final ObjectThatContainsData data1 = HelloWorld.staticData1;
    private final ObjectThatContainsData data2 = HelloWorld.staticData2;

    @Override
    public void start(Stage primaryStage) {

        String Text = "Hello "+data1+" "+data2+" World!";
        primaryStage.setTitle(Text);
        Button btn = new Button();
        btn.setText("Say '"+Text+"'");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(btn);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.setX(0);
        primaryStage.setY(0);
        primaryStage.show();
    }
}

class ObjectThatContainsData {
    public final String data;

    ObjectThatContainsData(String data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return data;
    }
}