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

在JavaFX中启动屏幕后尝试切换阶段,javafx,fxmlloader,Javafx,Fxmlloader,在加载所有必要的资源并打开主阶段之前,我一直在尝试显示初始屏幕,但我一直在运行InvocationTargetException 换句话说,我的初级阶段加载一个FXML,它有一个如下所示的控制器: public class SplashController { @FXML VBox splashScreenVBox = new VBox(); @FXML protected void initialize() throws InterruptedExcepti

在加载所有必要的资源并打开主阶段之前,我一直在尝试显示初始屏幕,但我一直在运行
InvocationTargetException

换句话说,我的初级阶段加载一个FXML,它有一个如下所示的控制器:

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();
}
错误是,我将指向一行,在该行上我第一次
fxmloader.load()
FXML(我有两个FXML文件,每个阶段一个)

有人能解释一下为什么会发生这种情况,最好是如何正确使用
fxmloader
,以防我所做的是一个问题吗

编辑:Stacktrace

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

从堆栈跟踪中可以看出,在
SplashController
联机
18
initialize
方法中,您得到了
NullPointerException
。我假设第18行是这一行:

Stage primaryStage = (Stage) splashScreenVBox.getScene().getWindow();
问题很可能是这个调用:
getScene().getWindow()
。方法
getScene()
将在此处返回
null
,因为
splashScreenVBox
还不是
场景的一部分。怎么可能呢?在执行
fxmloader.load()
期间调用
initialize
方法。这意味着您还没有机会将
fxmloader.load()
的结果添加到
场景中

要解决此问题,一个选项是向
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);
我将
splashtage
传递给该方法,以便您可以在准备显示主
Stage
时隐藏/关闭它。根据代码的设计,除了传递参数外,可能还有其他方法来实现这一点


如何创建一个抽象控制器的示例,该控制器将在根添加到
场景
并且
场景
已添加到
窗口
时自动调用方法

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

  }

}
import java.util.function.Consumer;
导入javafx.beans.value.ChangeListener;
导入javafx.beans.value.observeValue;
导入javafx.fxml.fxml;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.stage.Window;
公共抽象类抽象控制器{
//受保护,因此子类可以访问根
//直接。你也可以把它藏在
//盖特。
@FXML保护T根;
//重写此方法的子类必须调用
//超级实现
@FXML
受保护的void初始化(){
Consumer onNewWindow=this::onAddedToWindow;
消费者onNewScene=场景->
scene.windowProperty().addListener(新的SelfRemovingChangeListener(onNewWindow));
root.sceneProperty().addListener(新的SelfRemovingChangeListener(onNewScene));
}
在AddedToWindow(窗口窗口)上保护抽象空;
私有静态类SelfRemovingChangeListener实现ChangeListener{

private final consumer放置
InvocationTargetException
的整个堆栈跟踪。另外,不要初始化
FXML
注释字段:
VBox splashScreenVBox=new VBox()
。您创建的实例将被
fxmloader
@Slaw注入的实例替换。谢谢,我已经更正了
@FXML
注释字段,并在stacktrace中进行了编辑。我应该补充一点,场景/阶段将同时运行一段时间,直到第一个场景/阶段关闭,但这从co中是可见的de.这不是FXMLLoader的问题吗?哦,我终于明白了。正如你所说,我试图从一个还不存在的对象中获取场景,
在初始化()期间
。非常感谢您清理这些。我已经编辑了代码,现在它完全按照预期工作。有没有什么方法可以代替
initialize()
以“加载后”方式自动调用的方法?@Dropout从技术上讲,
initialize
方法是“加载后”方法方法。您希望在根添加到
场景后自动调用一个方法,并且
场景添加到
阶段后自动调用该方法,对吗?问题是发生在
FXMLLoader
的控制之外。它不知道何时会发生这种情况。最接近的方法是创建一个将添加此行为。我将用一个示例编辑我的答案。谢谢,这真的很有帮助!@Dropout example AddedHanks!非常感谢!