2017-12-28 13 views
0

실제 프로젝트에서 발생하는 오류를 재현 할 수 있도록 작은 프로그램을 만들었습니다. 두 개의 .fxml 파일을 담당하는 MainWindow.java 컨트롤러 클래스가 있습니다 : MainWindow.fxmlAnchorTest.fxml. 컨트롤러 클래스의

코드 :Scene Builder 8.0이 포함 된 JavaFX의 setCenter() 메소드로 Null 포인터 예외 가져 오기

package projecterror.controller; 

import java.io.IOException; 

import javafx.application.Application; 
import javafx.fxml.FXML; 
import javafx.fxml.FXMLLoader; 
import javafx.geometry.Rectangle2D; 
import javafx.scene.Scene; 
import javafx.scene.control.Menu; 
import javafx.scene.control.MenuItem; 
import javafx.scene.layout.AnchorPane; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.Screen; 
import javafx.stage.Stage; 

public class MainWindow extends Application { 

    private Stage primaryStage; 
    private BorderPane mainWindow; 

    @FXML 
    Menu menuFile, menuAnalysis; 
    @FXML 
    MenuItem menuNew; 


    @FXML 
    private void initialize() { 
     menuNew.setOnAction((event) -> { 
      try { 
       FXMLLoader loader = new FXMLLoader(); 
       loader.setLocation(getClass().getResource("../view/AnchorTest.fxml")); 
       AnchorPane anchorTest = (AnchorPane) loader.load(); 
       mainWindow.setCenter(anchorTest); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     }); 
    } 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     this.primaryStage = primaryStage; 
     this.primaryStage.setTitle("Test Project"); 

     initMainWindow(); 

    } 

    public void initMainWindow() { 
     try { 
      FXMLLoader loader = new FXMLLoader(); 
      loader.setLocation(getClass().getResource("../view/MainWindow.fxml")); 
      mainWindow = (BorderPane) loader.load(); 

      Scene scene = new Scene(mainWindow); 
      primaryStage.setScene(scene); 

      //Fullscreen 
      Screen screen = Screen.getPrimary(); 
      Rectangle2D bounds = screen.getVisualBounds(); 
      primaryStage.setX(bounds.getMinX()); 
      primaryStage.setY(bounds.getMinY()); 
      primaryStage.setWidth(bounds.getWidth()); 
      primaryStage.setHeight(bounds.getHeight()); 

      primaryStage.setResizable(false); 
      primaryStage.show(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

} 

MainWindow.fxmltopMenuBar이있는 BorderPane이다.

코드 :

<?xml version="1.0" encoding="UTF-8"?> 

<?import javafx.scene.control.Menu?> 
<?import javafx.scene.control.MenuBar?> 
<?import javafx.scene.control.MenuItem?> 
<?import javafx.scene.control.SeparatorMenuItem?> 
<?import javafx.scene.layout.BorderPane?> 

<BorderPane style="-fx-background-color: #DCDCDC;" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="projecterror.controller.MainWindow"> 
    <top> 
     <MenuBar BorderPane.alignment="CENTER"> 
     <menus> 
      <Menu fx:id="menuFile" mnemonicParsing="false" text="File"> 
      <items> 
        <Menu fx:id="menuAnalysis" mnemonicParsing="false" text="Analysis"> 
        <items> 
         <MenuItem fx:id="menuNew" mnemonicParsing="false" text="New" /> 
        </items> 
        </Menu> 
      </items> 
      </Menu> 
     </menus> 
     </MenuBar> 
    </top> 
</BorderPane> 


AnchorTest.fxml 아이들이 AnchorPane로있는 AnchorPane입니다.

코드는 : 나는 메뉴 File > Analysis > New를 클릭 한 후

<?xml version="1.0" encoding="UTF-8"?> 

<?import java.lang.*?> 
<?import javafx.scene.control.*?> 
<?import javafx.scene.layout.*?> 
<?import javafx.scene.layout.AnchorPane?> 

<AnchorPane xmlns:fx="http://javafx.com/fxml/1"> 
    <children> 
      <AnchorPane layoutX="6.0" layoutY="450.0" /> 
      <AnchorPane layoutX="6.0" layoutY="569.0" prefHeight="116.0" prefWidth="730.0"> 
        <Button layoutX="6.0" layoutY="26.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="102.0" text="Button 1" /> 
        <Button layoutX="115.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 2" /> 
        <Button layoutX="226.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 3" /> 
        <Button layoutX="115.0" layoutY="75.0" mnemonicParsing="false" prefWidth="102.0" text="Button 4" /> 
        <Button layoutX="383.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 5" /> 
        <Button layoutX="491.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 6" /> 
        <Button layoutX="600.0" layoutY="26.0" mnemonicParsing="false" prefWidth="102.0" text="Button 7" /> 
        <Button layoutX="491.0" layoutY="75.0" mnemonicParsing="false" prefWidth="102.0" text="Button 8" /> 
        <Button layoutX="600.0" layoutY="75.0" mnemonicParsing="false" prefWidth="102.0" text="Button 9" /> 
        <TextField alignment="TOP_CENTER" layoutX="341.0" layoutY="26.0" prefHeight="25.0" prefWidth="28.0" text="10" /> 
      </AnchorPane> 
     </children> 
</AnchorPane> 


MainWindow.fxml에는, MainWindow.fxml 센터로 AnchorTest.fxml 내용을 배치 할 예정이다. NPE 않다고 생각한다

Exception in thread "JavaFX Application Thread" java.lang.NullPointerException 
     at projecterror.controller.MainWindow.lambda$0(MainWindow.java:35) 
     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.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) 
     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.control.MenuItem.fire(MenuItem.java:462) 
     at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1405) 
     at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.lambda$createChildren$343(ContextMenuContent.java:1358) 
     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.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:380) 
     at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:294) 
     at java.security.AccessController.doPrivileged(Native Method) 
     at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$354(GlassViewEventHandler.java:416) 
     at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) 
     at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:415) 
     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) 

라인입니다 : 나는이 유사하지만 대부분 자신의 문제 모두에 관한 다른 질문을 읽고

mainWindow.setCenter(anchorTest); 

그러나, 나는 다음과 같은 스택 추적 오류 fxml 파일의 경로. 나는 실제 프로젝트에서 다른보기에 대해 동일한 유형을 사용하고 지금까지는 문제가 없기 때문에 이것이 문제가 아니라고 단호하게 믿습니다.

누구든지 프로그램을 실행하고 싶다면 here 프로젝트를 업로드했습니다. 사전에

Project structure

감사 어떤 도움을 크게 감상 할 수있다 :

프로젝트 구조는 다음과 같습니다!

+0

이런 방식으로 'JavaFX Main'구조를 본 적이 없습니다. – Sedrick

+0

또한 프로젝트 구조를 게시해야합니다. – Sedrick

+1

'mainWindow'는 컨트롤러에서 초기화되지 않았습니다; 응용 프로그램이 시작될 때 ('start()'가 호출 된 인스턴스) 생성 된'Application' 인스턴스에서 초기화됩니다. 여기서 핵심은 애플리케이션 클래스를 컨트롤러 클래스로 사용해서는 안된다는 것이다. 그것은 너무 혼란 스럽습니다 (그리고 "Single Responsibility"와 같은 모든 종류의 기본 OOP 설계 원칙을 위반합니다). –

답변

2

JavaFX 응용 프로그램을 시작하면 응용 프로그램 클래스의 인스턴스가 만들어지고 (다른 일들 중에서도) start() 메서드가 FX 응용 프로그램 스레드의 Application 인스턴스에서 호출됩니다.

FXML 파일을로드 할 때 FXML 파일에서 컨트롤러 클래스를 지정하면 해당 컨트롤러 클래스의 인스턴스가 만들어지고 @FXML- 주석이 지정된 필드가 해당 인스턴스에 주입되고 initialize() 메서드가 호출됩니다. 이 모든 것은 FXMLLoader.load()으로 전화하는 동안 발생합니다.

결과적으로 코드에서 MainWindow이라는 두 개의 서로 다른 인스턴스로 끝납니다. 한 인스턴스에서 start() 메서드를 호출하면 mainWindow 필드가 초기화되고 다른 인스턴스에서는 initialize() 메서드가 호출됩니다. mainWindow은 두 번째 인스턴스에서 초기화되지 않았으므로 initialize() 메서드에서 역 참조하려고하면 null 포인터 예외가 발생합니다.

Application 클래스에서 컨트롤러 클래스를 분리하십시오.

package projecterror.controller; 

import java.io.IOException; 

import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.geometry.Rectangle2D; 
import javafx.scene.Scene; 
import javafx.scene.layout.BorderPane; 
import javafx.stage.Screen; 
import javafx.stage.Stage; 

public class MainWindow extends Application { 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     primaryStage.setTitle("Test Project"); 

     initMainWindow(primaryStage); 

    } 

    public void initMainWindow(Stage primaryStage) { 
     try { 
      FXMLLoader loader = new FXMLLoader(); 
      loader.setLocation(getClass().getResource("../view/MainWindow.fxml")); 
      BorderPane mainWindow = loader.load(); 

      Scene scene = new Scene(mainWindow); 
      primaryStage.setScene(scene); 

      //Fullscreen 
      Screen screen = Screen.getPrimary(); 
      Rectangle2D bounds = screen.getVisualBounds(); 
      primaryStage.setX(bounds.getMinX()); 
      primaryStage.setY(bounds.getMinY()); 
      primaryStage.setWidth(bounds.getWidth()); 
      primaryStage.setHeight(bounds.getHeight()); 

      primaryStage.setResizable(false); 
      primaryStage.show(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

} 
package projecterror.controller; 

import java.io.IOException; 

import javafx.fxml.FXML; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.control.Menu; 
import javafx.scene.control.MenuItem; 
import javafx.scene.layout.AnchorPane; 
import javafx.scene.layout.BorderPane; 

public class MainWindowController { 

    @FXML 
    private BorderPane mainWindow; 

    @FXML 
    Menu menuFile, menuAnalysis; 
    @FXML 
    MenuItem menuNew; 


    @FXML 
    private void initialize() { 
     menuNew.setOnAction((event) -> { 
      try { 
       FXMLLoader loader = new FXMLLoader(); 
       loader.setLocation(getClass().getResource("../view/AnchorTest.fxml")); 
       AnchorPane anchorTest = (AnchorPane) loader.load(); 
       mainWindow.setCenter(anchorTest); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     }); 
    } 



} 

에 컨트롤러 위해서는 : 전체 (일반적으로는 응용 프로그램을 시작하는 데 필요한 코드를 구현해야합니다)로 응용 프로그램의 수명주기를 관리하는 것보다 Application 클래스는 다른 아무것도하지 않고해야 BorderPane에 액세스하려면 FXML에서 주입해야합니다 (위의 컨트롤러 코드에서 주석을 참고하십시오). FXML 파일의 요소에 fx:id을 추가하십시오.

<BorderPane style="-fx-background-color: #DCDCDC;" fx:id="mainWindow" 
    xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" 
    fx:controller="projecterror.controller.MainWindowController"> 
+0

완전성을 위해 MainWindow 클래스 내에서 private stage primaryStage;를 잊어 버린 것 같습니다. 그것은 당신의 설명을 위해 완벽하게 작동합니다. – leandrocoutom

+1

@leandrocoutom 사실,'start()'와'initMainWindow()'메소드에서만 필요하기 때문에 실제로 생략하고 싶었지만 변경을 완료하지는 못했습니다. 내가 고칠 .... –