2017-08-28 71 views
0

我有一个StackPane作为我的根,并在主应用程序的Start()方法中加载了一个BorderPane布局。 在BorderPane布局中,我有2个按钮:一个用于添加一个fxml文件(Menu.fxml),另一个用于删除该文件。 一旦加载Menu.fxml,我在Menu.fxml中有一个按钮,点击时应该在以前加载的BorderPane布局的中心加载一个fxml文件(One.fxml)。 但是,在这个阶段,我得到一个空指针异常。你能指出我哪里错了吗? 谢谢。JavaFX8空指针从控制器加载FXML文件时出现异常

下面是我的代码:


public class Main extends Application { 

    private static StackPane rootStack; 
    private static BorderPane rootBorder; 

    @Override 
    public void start(Stage stage) throws Exception { 
     rootStack = FXMLLoader.load(getClass().getResource("Base.fxml")); 
     rootBorder = FXMLLoader.load(getClass().getResource("Border.fxml")); 
     rootStack.getChildren().addAll(rootBorder); 

     Scene scene = new Scene(rootStack); 
     stage.setScene(scene); 
     stage.show(); 
    } // start 

    public static StackPane getRootStack() { 
     return rootStack; 
    } // end of method getRootStack 

    public static BorderPane getRootBorder() { 
     return rootBorder; 
    } // end of method getRootBorder 

    public static void main(String[] args) { 
     launch(args); 
    } // launch 

} // Main 

public class BorderController { 

    private StackPane rootStack = Main.getRootStack(); 
    VBox menu; 

    public void initialize() throws IOException { 
     menu = FXMLLoader.load(getClass().getResource("Menu.fxml")); 
    } // initialize  

    @FXML 
    private void addMenuPane(ActionEvent event) throws IOException { 
     rootStack.getChildren().add(menu); 
    } // addMenuPane 

    @FXML 
    private void removeNode(ActionEvent event) { 
     rootStack.getChildren().remove(menu); 
    } // removeNode 

} // BorderController 

public class MenuController { 

    private BorderPane rootBorder = Main.getRootBorder(); 

    @FXML 
    private JFXButton oneButton; 

    public void initialize() { 
     oneButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (event) -> { 
      try { 
       rootBorder.setCenter(FXMLLoader.load(getClass().getResource("One.fxml"))); 
      } catch (IOException ex) { 
       Logger.getLogger(MenuController.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     }); 

    } // initialize  

} // MenuController 

堆栈跟踪:

Executing C:\Users\User\Documents\NetBeansProjects\Stack\dist\run2093523570\Stack.jar using platform C:\Program Files\Java\jdk1.8.0_121\jre/bin/java 
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException 
    at stack.MenuController.lambda$initialize$0(MenuController.java:22) 
    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$ClickGenerator.postProcess(Scene.java:3470) 
    at javafx.scene.Scene$ClickGenerator.access$8100(Scene.java:3398) 
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3766) 
    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:381) 
    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$354(GlassViewEventHandler.java:417) 
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) 
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:416) 
    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$148(WinApplication.java:191) 
    at java.lang.Thread.run(Thread.java:745) 
Deleting directory C:\Users\User\Documents\NetBeansProjects\Stack\dist\run2093523570 
+0

在问题中发布完整的堆栈跟踪。 –

+0

它看起来像'rootBorder'为空。你可以检查吗? –

+0

我可以读你的代码。我要求您在'MenuController'的事件处理程序中验证'rootBorder'为空。只需将一条调试行放入事件处理程序中,例如'if(rootBorder == null)System.out.println(“Warning:rootBorder is null”);'看看是否显示消息。或者在事件处理程序的开始处放置一个断点并在调试器中运行它。 –

回答

2

当加载一个FXML文件,如果FXML文件的根元素具有fx:controller属性,然后按该属性指定的类被实例化,任何@FXML -annotated字段被注入,并且initialize()方法被调用上控制器类实例(如果有的话)。所有这些都是方法FXMLLoader的一部分。

如果您考虑代码行的Application.start()方法:

rootBorder = FXMLLoader.load(getClass().getResource("Border.fxml")); 

这将加载Border.fxml,创建控制器,调用其initialize()方法等,只有当一切完成后,将分配结果为rootBorderBorderController中的initialize()方法被调用加载Menu.fxml;然后在Menu.fxml中实例化控制器(我认为)是MenuController。对于MenuController的初始化代码包括

private BorderPane rootBorder = Main.getRootBorder(); 

注意,在这一点上,load()方法加载Border.fxml还没有完成,所以在Main静态rootBorder领域尚未分配,因此Main.getRootBorder()回报null

因此,以后当你尝试做

rootBorder.setCenter(...); 

因为rootBorder被分配值null,你会得到一个空指针异常。

一个快速和肮脏的解决将是公正的时刻检索边境窗格中,您需要:

public class MenuController { 

    @FXML 
    private JFXButton oneButton; 

    public void initialize() { 
     oneButton.addEventHandler(MouseEvent.MOUSE_CLICKED, (event) -> { 
      try { 
       Main.getRootBorder().setCenter(FXMLLoader.load(getClass().getResource("One.fxml"))); 
      } catch (IOException ex) { 
       Logger.getLogger(MenuController.class.getName()).log(Level.SEVERE, null, ex); 
      } 
     }); 

    } // initialize  

} // MenuController 

Application子类中使用静态字段的这种做法,但是,是一个非常糟糕的设计。你会介绍各种不必要的耦合(你的MenuController实际上应该依赖于那个特殊的Main类的存在?)并且公开所有不应该被暴露的UI元素(假设你想改变总体布局在有一点,你基本上需要改变你的整个UI代码库)。你应该重组这个以避免需要这样做。

+0

谢谢James_D花时间解释导致此问题的原因。真的很感激它。我在Main类中使用静态字段的唯一原因是因为我想要访问其他控制器类中的BorderPane布局以根据用户选择的菜单加载。除了在互联网上搜索教程(导致上述结果)之外,我没有其他资源可以指导我朝着正确的方向发展。我非常喜欢JavaFX的工作,并希望学习正确的方式来重组这个特定的项目。 – snow

+0

有没有一个很好的设计例子可以让我指出这个场景,以便我可以从中学习?我一直在使用Dietel Java How to Program教科书来学习JavaFX,但它的材料有限,仅涵盖一些基本概念。有什么好的参考资料可以用来更详细地学习JavaFX吗?我真的很想学习和提高自己的知识。谢谢你的帮助。 – snow

+0

@snow看看github.com/acaicedo/Screen-Framework。如果我有时间,我会详细说明答案。 –