在JavaFX中启动屏幕后尝试切换阶段

时间:2018-08-20 10:06:12

标签: javafx fxmlloader

我试图显示一个初始屏幕,直到加载所有必要的资源并随后打开主界面为止,但是我一直遇到InvocationTargetException

换句话说,我的主要阶段加载了一个FXML,其中包含一个如下所示的Controller:

public class SplashController {

    @FXML
    VBox splashScreenVBox = new VBox();

    @FXML
    protected void initialize() throws InterruptedException, IOException {

        Stage primaryStage = (Stage) splashScreenVBox.getScene().getWindow();
        primaryStage.close();
        new MainStage();            
    }
}

MainStage类只是加载FXML并显示场景:

public MainStage() throws IOException {

    Parent root = FXMLLoader.load(getClass().getResource("/core/views/Main.fxml"));
    this.setScene(new Scene(root, 800, 600));
    this.show();
}

我遇到的错误指向我第一次FXMLLoader.load() FXML所在的行(我有两个FXML文件,每个阶段一个)。

请问有人可以说明为什么会发生这种情况,最好是如何正确使用FXMLLoader,以防万一我正在做的事情有问题?

编辑:堆栈跟踪

Exception in Application start method
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
    at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$155(LauncherImpl.java:182)
    at java.lang.Thread.run(Thread.java:745)
Caused by: javafx.fxml.LoadException: 
/C:/Users/REDACTED/out/production/REDACTED/core/views/Splash.fxml

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2571)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
    at core.Scenes.start(Scenes.java:19)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    ... 1 more
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2566)
    ... 17 more
Caused by: java.lang.NullPointerException
    at core.controllers.SplashController.initialize(SplashController.java:18)
    ... 28 more
Exception running application core.Scenes

1 个答案:

答案 0 :(得分:1)

从堆栈跟踪中可以看到,您在NullPointerException行的initialize的{​​{1}}方法中得到了SplashController。我假设18行是这一行:

18

问题很可能是此电话:Stage primaryStage = (Stage) splashScreenVBox.getScene().getWindow(); 。方法getScene().getWindow()将在此处返回getScene(),因为null还不是splashScreenVBox的一部分。怎么会这样? Scene方法在执行initialize的过程中称为。这意味着您还没有机会将FXMLLoader.load()的结果添加到FXMLLoader.load()中。

要解决此问题,请在您的Scene中添加一种方法来加载SplashController

MainStage

我将FXMLLoader loader = new FXMLLoader(getClass().getResource("your/resource")); Parent root = loader.load(); Stage splashStage = new Stage(); splashStage.setScene(new Scene(root)); splashStage.show(); SplashController controller = loader.getController(); controller.loadMainApp(splashStage); 传递给该方法,以便在准备显示主splashStage时可以将其隐藏/关闭。根据代码的设计,除了传递参数外,还有其他方法可以实现。


如何创建一个抽象控制器的示例,当根添加到StageScene已经添加到Scene时,该抽象控制器将自动调用方法。

Window

这要求您的FXML文件中有一个import java.util.function.Consumer; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Scene; import javafx.stage.Window; public abstract class AbstractController<T extends Node> { // protected so subclasses can access the root // directly. You could also hide this behind a // getter. @FXML protected T root; // Subclasses that override this method must call the // super implementation @FXML protected void initialize() { Consumer<Window> onNewWindow = this::onAddedToWindow; Consumer<Scene> onNewScene = scene -> scene.windowProperty().addListener(new SelfRemovingChangeListener<>(onNewWindow)); root.sceneProperty().addListener(new SelfRemovingChangeListener<>(onNewScene)); } protected abstract void onAddedToWindow(Window window); private static class SelfRemovingChangeListener<T> implements ChangeListener<T> { private final Consumer<? super T> onNewValue; private SelfRemovingChangeListener(Consumer<? super T> onNewValue) { this.onNewValue = onNewValue; } @Override public void changed(ObservableValue<? extends T> observable, T oldValue, T newValue) { onNewValue.accept(newValue); observable.removeListener(this); } } } ,并且根对象是fx:id="root"。将Node添加到onAddedToWindow时,仅将root称为 first 。您仍然必须将Window添加到其他地方的root中(无论您叫Window是什么)。