2017-08-28 3 views
0

J'ai un StackPane en tant que racine et j'ai chargé une disposition BorderPane dans la méthode Main Application Start(). Dans la mise en page BorderPane j'ai 2 boutons: Un pour ajouter un fichier fxml (Menu.fxml) et l'autre pour supprimer ce fichier. Une fois le menu.fxml chargé, j'ai un bouton dans Menu.fxml qui, lorsqu'il est cliqué, devrait charger un fichier fxml (One.fxml) au centre de la mise en page BorderPane précédemment chargé. Cependant, à ce stade, j'obtiens une exception de pointeur nul. Pouvez-vous me dire où je me trompe? Merci.JavaFX8 Exception de pointeur nul lors du chargement d'un fichier FXML du contrôleur

Ci-dessous mon code:


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 

Stack Trace:

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

Affichez la trace de pile complète dans la question. –

+0

Il semble que 'rootBorder' soit nul. Pouvez-vous vérifier cela? –

+0

Je peux lire votre code. Je vous demande de vérifier que 'rootBorder' est null dans le gestionnaire d'événements dans' MenuController'. Mettez simplement une ligne de débogage dans le gestionnaire d'événements, par ex. 'if (rootBorder == null) System.out.println (" Attention: rootBorder est null ");' et voit si le message est affiché. Ou mettez un point d'arrêt au début du gestionnaire d'événements et exécutez-le dans votre débogueur. –

Répondre

2

Lorsque vous chargez un fichier FXML, si l'élément racine du fichier FXML a un attribut fx:controller, puis la classe spécifiée par cet attribut est instancié, les @FXML champs -annotated sont injectés, et la méthode initialize() est invoquée sur l'instance de classe de contrôleur (s'il y en a une). Tout cela se produit dans le cadre de la méthode load() de FXMLLoader.

Si vous considérez la ligne de code dans votre Application.start() méthode:

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

cela chargera Border.fxml, créez le contrôleur, appeler sa méthode initialize(), etc, et seulement quand tout ce qui est complète sera elle affecter le résultat à rootBorder. La méthode initialize() dans BorderController qui est appelée charge Menu.fxml; qui à son tour instancie le contrôleur dans Menu.fxml qui (je suppose) est MenuController. Le code initialiseur pour MenuController comprend

private BorderPane rootBorder = Main.getRootBorder(); 

Notez que, à ce stade, la méthode load() qui charge Border.fxml est toujours pas terminé, donc le champ rootBorder statique Main n'a pas encore été attribué, et ainsi Main.getRootBorder() retours null .

Par conséquent, plus tard, quand vous essayez de faire

rootBorder.setCenter(...); 

depuis rootBorder a été affecté la valeur null, vous obtenez une exception de pointeur nul.

Une solution rapide et sale serait juste pour récupérer le volet frontière au moment où vous en avez besoin:

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 

Cette approche de l'utilisation des champs statiques dans la sous-classe Application, cependant, est une conception vraiment mauvais.Vous introduisez toutes sortes de couplages inutiles (est-ce que votre MenuController est vraiment supposé s'appuyer sur l'existence de cette classe Main particulière?) Et expose toutes sortes d'éléments d'interface utilisateur qui ne devraient pas être exposés (supposons que vous voulez changer la disposition générale à un certain point, vous devriez fondamentalement changer toute votre base de code d'interface utilisateur). Vous devriez probablement restructurer cela pour éviter de devoir faire les choses de cette façon.

+0

Merci James_D d'avoir pris le temps d'expliquer ce qui cause ce problème. J'apprécie beaucoup. La seule raison pour laquelle j'utilisais un champ statique dans la classe Main est que je voulais que l'accès à la disposition BorderPane dans d'autres classes de contrôleur soit chargé selon le menu sélectionné par l'utilisateur. En dehors de la recherche dans les tutoriels sur internet (ce qui a conduit au résultat ci-dessus), je n'ai pas d'autre recours qui puisse me guider dans la bonne direction. J'apprécie vraiment travailler sur JavaFX et je veux apprendre la bonne manière de restructurer ce projet particulier. – snow

+0

Y a-t-il un bon exemple de conception pour ce scénario que vous pourriez me montrer afin que je puisse en tirer des leçons? J'ai utilisé le livre de programmation de Dietel Java pour apprendre JavaFX mais il a un contenu limité et ne couvre que quelques concepts de base. Y a-t-il de bons matériaux de référence que je peux utiliser pour apprendre JavaFX plus en détail? Je veux vraiment apprendre et améliorer mes connaissances. Je vous remercie pour votre aide. – snow

+0

@snow Jetez un oeil à github.com/acaicedo/Screen-Framework. Si j'ai le temps plus tard, je vais élaborer sur la réponse. –