JavaFX - 主控制器调用子控制器的函数,导致空指针异常



我对JavaFX比较陌生,但我尽量说清楚。

问题:当我的主控制器调用我的子控制器的函数时,只要它不需要操作 fxml 文件的内容,它就可以完全正常运行。

令人惊讶的是,如果在初始化函数中操作 fxml 注释组件,则代码不会导致任何错误。但是每当我想在其他函数中使用它们时,它都会抛出一个 NullPointerException。

因此,为了在fxml注释组件下更清楚一点,我的意思是例如:@FXML私有标签示例Lbl。

我创建了一个简化的示例:

主控制器类:

package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Tab;
import javafx.scene.layout.AnchorPane;
public class MainController implements Initializable {
@FXML SubController subController;
@FXML private Tab Tab1;
@Override
public void initialize(URL location, ResourceBundle resources) {
subController = new SubController();
FXMLLoader loader = new FXMLLoader();
try {
AnchorPane anch1 = loader.load(getClass().getResource("SubView.fxml"));
Tab1.setContent(anch1);
}
catch(Exception e) {
System.out.println("unable to load tab1");
e.printStackTrace();
}
}
public void fooFunction() {
if(subController != null) {
subController.testFunction();   
}
}
}

子控制器类:

package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
public class SubController implements Initializable {
@FXML private Label exampleLbl;
@Override
public void initialize(URL arg0, ResourceBundle arg1) {
if(exampleLbl != null) {
System.out.println("It is printed, so it is not null");
exampleLbl.setText("So I can manipulate this as I want.");
}
}
public void testFunction() {
if(exampleLbl == null) {
System.out.println("It is printed, so it is null now.");
exampleLbl.setText("But not here. It will throw an error.");
}
}
}

nullptrexception 发生在 testFunction() 方法第 26 行的子控制器类上。

行:exampleLbl.setText("但不是在这里。它将抛出错误。

所以它在初始化函数中工作正常,但在测试函数中不能正常工作

这是我的FXML文件:

MainView fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<center>
<TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab fx:id="Tab1" text="Test">
<content>
</content>
</Tab>
</tabs>
</TabPane>
</center>
<bottom>
<Button mnemonicParsing="false" onAction="#fooFunction" text="Button" BorderPane.alignment="CENTER" />
</bottom>
</BorderPane>

子视图 FXML:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.SubController">
<Label fx:id="exampleLbl" layoutX="5.0" layoutY="128.0" prefHeight="17.0" prefWidth="188.0" text="Label" />
</AnchorPane>

以下是错误消息和控制台输出(如果有帮助):

It is printed, so it is not null
It is printed, so it is null now.
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1774)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1657)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Node.fireEvent(Node.java:8411)
at javafx.scene.control.Button.fire(Button.java:185)
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:182)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:96)
at com.sun.javafx.scene.control.skin.BehaviorSkinBase$1.handle(BehaviorSkinBase.java:89)
at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
at javafx.event.Event.fireEvent(Event.java:198)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3757)
at javafx.scene.Scene$MouseHandler.access$1500(Scene.java:3485)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1762)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2494)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:394)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$358(GlassViewEventHandler.java:432)
at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:431)
at com.sun.glass.ui.View.handleMouseEvent(View.java:555)
at com.sun.glass.ui.View.notifyMouse(View.java:937)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$152(WinApplication.java:177)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.Trampoline.invoke(Unknown Source)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1771)
... 48 more
Caused by: java.lang.NullPointerException
at application.SubController.testFunction(SubController.java:26)
at application.MainController.fooFunction(MainController.java:36)
... 58 more

因此,如果有人能解释我在这里错过了什么并为我提供简单的解决方案,我将不胜感激。

使用subController = new SubController()时,刚刚创建的实例与 FXML 文件无关。不会注入任何字段,因为控制器实例不是由FXMLLoader管理的。在您的情况下,使用以下方法应该可以解决问题:

@Override
public void initialize(URL location, ResourceBundle resources) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("SubView.fxml"));
try {
AnchorPane anch1 = loader.load();
subController = loader.getController();
Tab1.setContent(anch1);
} catch (Exception e) {
System.out.println("unable to load tab1");
e.printStackTrace();
}
}

请注意两件事:

  1. 我在施工期间设置了FXMLLoader的位置。当您使用loader.load(/* URL */)时,您实际上是在使用静态FXMLLoader#load(URL)方法。您需要使用实例load()方法。
  2. 我将subController字段(顺便说一下,不需要用@FXML注释)分配loader.getController().该方法将返回从您通过 FXML 文件中的fx:controller属性指定的类名创建的FXMLLoader实例。重要提示:您必须在load()后拨打getController()

您可能还需要考虑使用fx:include.

MainView.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
<center>
<TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE">
<tabs>
<Tab fx:id="Tab1" text="Test">
<content>
<!-- replace the value of source as needed -->
<fx:include source="SubView.fxml" fx:id="sub"/>
</content>
</Tab>
</tabs>
</TabPane>
</center>
<bottom>
<Button mnemonicParsing="false" onAction="#fooFunction" text="Button" BorderPane.alignment="CENTER" />
</bottom>
</BorderPane>

这使得不再需要在MainController#initialize方法中手动加载SubView.fxml文件。此外,由于我为包含的文件分配了fx:id,因此将为您注入SubController实例。在注入控制器时,FXMLLoader会查找名称为<fx:id>Controller的适当类型(和注释)字段(请参阅嵌套控制器)。

initialize中创建的SubController实例不与 fxml 文件一起使用。FXMLLoader创建一个不同的SubController实例,注入Label并为其调用initialize

您需要使用非staticload方法加载 fxml 文件,然后检索FXMLLoader创建的控制器。(load(URL)static。注释没有注入@FXML(subController)的字段是没有必要的。

主控制器

@Override
public void initialize(URL location, ResourceBundle resources) {
FXMLLoader loader = new FXMLLoader(getClass().getResource("SubView.fxml"));
try {
AnchorPane anch1 = loader.load();
Tab1.setContent(anch1);
subController = loader.getController(); // retrieve controller
} catch(Exception e) {
System.out.println("unable to load tab1");
e.printStackTrace();
}
}

最新更新