Swing, JavaFx - Felder aus FXML sind null

GesibaIT

Mitglied
Hallo Community,
dies ist unser erster Post hier im Forum, doch sicher nicht unser letzter, im positiven, wie negativen Sinn 😀

Projektdetails:
- Maven Projekt mit Archetype
- JDK 1.8
- eingebetet ist es in ein vorhandenes Swing Framework

Kurzbeschreibung:
Die Anzeige des Guis über fxml funktioniert. Jedoch der Zugriff in der initialize Methode auf die enthaltenen Felder im fxml bringt eine NullPointerException, da diese nicht initialisiert sind.
Ich hoffe, jemand kann uns helfen 🙂

Ganze Exception:
Java:
Sep 24, 2014 1:40:17 PM at.gesiba.buildingdoc.view.BuildingDocPresenter loadScreen
SCHWERWIEGEND: null
javafx.fxml.LoadException: 
file:/C:/.m3/at/gesiba/giba/plugins/buildingdoc/14.13-SNAPSHOT/buildingdoc-14.13-SNAPSHOT.jar!/fxml/buildingDocStammdaten.fxml

	at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2617)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2595)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
	at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
	at at.gesiba.buildingdoc.view.BuildingDocPresenter.loadScreen(BuildingDocPresenter.java:30)
	at at.gesiba.buildingdoc.view.BuildingDocViewController.initFX(BuildingDocViewController.java:53)
	at at.gesiba.buildingdoc.view.BuildingDocViewController.access$000(BuildingDocViewController.java:21)
	at at.gesiba.buildingdoc.view.BuildingDocViewController$1.lambda$run$0(BuildingDocViewController.java:46)
	at at.gesiba.buildingdoc.view.BuildingDocViewController$1$$Lambda$9/1843246775.run(Unknown Source)
	at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:301)
	at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:298)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:298)
	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.access$300(WinApplication.java:39)
	at com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
	at at.gesiba.buildingdoc.stammdaten.BuildingDocStammdatenController.initialize(BuildingDocStammdatenController.java:60)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
	... 16 more

1. Erster Controller, welcher über das Framework gestartet wird, Zugangspunkt ist hier die Methode "getPanel()" Danach wird dann die Methode init() vom Framework aufgerufen

Java:
public class BuildingDocViewController extends GibaPlugin {

    public static String stammdaten = "Stammdaten";
    public static String stammdatenFile = "/fxml/buildingDocStammdaten.fxml";

    JFXPanel fxPanel;
    JPanel swingPanel;

    @Override
    public JPanel getPanel() {

        if (swingPanel == null) {
            fxPanel = new JFXPanel();
            swingPanel = new JPanel(new BorderLayout());
            swingPanel.add(fxPanel, BorderLayout.CENTER);
        }
        return swingPanel;
    }

    @Override
    protected void init() {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                Platform.runLater(() -> initFX());
            }
        });
    }

    private void initFX() {
        BuildingDocPresenter buildingDocPresenter = new BuildingDocPresenter();
        buildingDocPresenter.loadScreen(BuildingDocViewController.stammdaten, BuildingDocViewController.stammdatenFile);
        BorderPane root = new BorderPane(buildingDocPresenter);
        Scene scene = new Scene(root, 500, 500);
        fxPanel.setScene(scene);
    }
}

2. Über initFx() Methode wird loadScreen Methode des Presenters aufgerufen und dort dann "myLoader.load()"

Java:
public class BuildingDocPresenter extends BorderPane {

    public BuildingDocPresenter() {
        super();
    }

    public boolean loadScreen(String name, String resource) {
        try {
            System.err.println("FXML resource: " + getClass().getResource(resource));
            FXMLLoader myLoader = new FXMLLoader(getClass().getResource(resource));
            Parent loadedScreen = myLoader.load();
            IBuildingDoc mainScreen = myLoader.getController();
            mainScreen.setScreenParent(this);

            // Je nach Controller wird das Panel an die richtige Stelle des Border Layouts gesetzt
            mainScreen.addNode(this, loadedScreen);
            return true;
        } catch (IOException ex) {
            Logger.getLogger(BuildingDocPresenter.class.getName()).log(Level.SEVERE, null, ex);
            return false;
        }
    }

}

3. Nach fertigen Laden wird richtig "initialize" des betreffenden Controllers aufgerufen. Alle Felder des fxml Files sind mit Annotation angegeben.

Java:
public class BuildingDocStammdatenController implements Initializable, IBuildingDoc {

    BuildingDocPresenter buildingDocPresenter;
    BuildingDocDataRetrievingController retrievingController = new BuildingDocDataRetrievingController();
    ObjektstammDaten objektstammDaten;
    
    @FXML
    private TextField objektnummer;

    @FXML
    private TextField objektbezeichnung;
    
    @FXML
    private TextField eigentuemer;
    
    @FXML
    private Date bezugsdatum;
    
    @FXML
    private TextField wohneinheiten;
    
    @FXML
    private TextField stiegen;
    
    @FXML
    private TextField garagen;
    
    @FXML
    private TextField foerderung;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        //objektstammDaten = retrievingController.getObjektstammDaten(new Obstdp("1", "2215", "1", "1"));
        //objektnummer.setText(GUIHelper.prettyObjektNummer(objektstammDaten.getObstdp()));
        //objektbezeichnung.setText(objektstammDaten.getObstdp().getObjektBezeichnung());
        objektnummer.setText("test feld 1");
        objektbezeichnung.setText("test feld 2");
        
    }

    @Override
    public void setScreenParent(BuildingDocPresenter page) {
        buildingDocPresenter = page;
    }

    @Override
    public void addNode(BorderPane bp, Parent node) {
        bp.setCenter(node);
    }

}

4. Verwendets Interface für die HauptController

Java:
public interface IBuildingDoc {

    public BuildingDocDataRetrievingController bddrc = new BuildingDocDataRetrievingController();

    public void setScreenParent(BuildingDocPresenter page);

    public void addNode(BorderPane bp, Parent node);

}

5. FXML

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

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

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="at.gesiba.buildingdoc.stammdaten.BuildingDocStammdatenController">
   <children>
      <BorderPane prefHeight="400.0" prefWidth="600.0">
         <top>
            <Label text="Hauptdaten" BorderPane.alignment="CENTER" />
         </top>
         <center>
            <GridPane BorderPane.alignment="CENTER">
              <columnConstraints>
                <ColumnConstraints halignment="LEFT" hgrow="SOMETIMES" maxWidth="200.0" minWidth="0.0" prefWidth="200.0" />
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="400.0" minWidth="10.0" prefWidth="250.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                  <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              </rowConstraints>
               <children>
                  <Label text="Objektnummer">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <Label prefHeight="27.0" prefWidth="127.0" text="Objektbezeichnung" GridPane.rowIndex="1">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <TextField id="objektnummer" GridPane.columnIndex="1">
                     <opaqueInsets>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </opaqueInsets>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </TextField>
                  <TextField id="objektbezeichnung" GridPane.columnIndex="1" GridPane.rowIndex="1">
                     <opaqueInsets>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </opaqueInsets>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </TextField>
                  <Label prefHeight="17.0" prefWidth="118.0" text="Eigentümer" GridPane.rowIndex="2">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <TextField id="eigentuemer" GridPane.columnIndex="1" GridPane.rowIndex="2">
                     <opaqueInsets>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </opaqueInsets>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </TextField>
                  <Label prefHeight="17.0" prefWidth="118.0" text="Bezugsdatum" GridPane.rowIndex="3">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <Label prefHeight="17.0" prefWidth="118.0" text="Wohneinheiten" GridPane.rowIndex="4">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <Label prefHeight="17.0" prefWidth="118.0" text="Stiegen" GridPane.rowIndex="5">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <Label prefHeight="17.0" prefWidth="118.0" text="Garagen" GridPane.rowIndex="6">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <Label prefHeight="17.0" prefWidth="118.0" text="Förderung" GridPane.rowIndex="7">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </Label>
                  <TextField id="wohneinheiten" GridPane.columnIndex="1" GridPane.rowIndex="4">
                     <opaqueInsets>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </opaqueInsets>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </TextField>
                  <TextField id="stiegen" GridPane.columnIndex="1" GridPane.rowIndex="5">
                     <opaqueInsets>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </opaqueInsets>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </TextField>
                  <TextField id="garagen" GridPane.columnIndex="1" GridPane.rowIndex="6">
                     <opaqueInsets>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </opaqueInsets>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </TextField>
                  <TextField id="foerderung" GridPane.columnIndex="1" GridPane.rowIndex="7">
                     <opaqueInsets>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </opaqueInsets>
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </TextField>
                  <DatePicker id="bezugsdatum" GridPane.columnIndex="1" GridPane.rowIndex="3">
                     <padding>
                        <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
                     </padding>
                  </DatePicker>
               </children>
            </GridPane>
         </center>
      </BorderPane>
   </children>
</Pane>
 
Java:
Caused by: java.lang.NullPointerException
	at at.gesiba.buildingdoc.stammdaten.BuildingDocStammdatenController.initialize(BuildingDocStammdatenController.java:60)
	at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2548)
	... 16 more

Hier steht doch genau die Zeile wo die Exception passiert.
Einfach mal in dieser Klasse nachschauen was in der Zeile steht.

Die Exception ist eigentlich auch selbsterklärend.

Java:
public class BuildingDocStammdatenController implements Initializable, IBuildingDoc {

    BuildingDocPresenter buildingDocPresenter;
    BuildingDocDataRetrievingController retrievingController = new BuildingDocDataRetrievingController();
    ObjektstammDaten objektstammDaten;
    
    @FXML
    private TextField objektnummer;

    @FXML
    private TextField objektbezeichnung;


    @Override
    public void initialize(URL location, ResourceBundle resources) {
        //objektstammDaten = retrievingController.getObjektstammDaten(new Obstdp("1", "2215", "1", "1"));
        //objektnummer.setText(GUIHelper.prettyObjektNummer(objektstammDaten.getObstdp()));
        //objektbezeichnung.setText(objektstammDaten.getObstdp().getObjektBezeichnung());
        objektnummer.setText("test feld 1");
        objektbezeichnung.setText("test feld 2");

Sicher das deine Objekte schon initialisiert sind?
 
Also wo die Exception passiert und was diese aussagt, verstehe ich schon.
Habe ich ja auch in der Beschreibung erläutert.

Eigentlich bin ich davon ausgegangen, das die "initialize" Methode erst dann aufgerufen wird, wenn über den FXMLLoader das Gui fertig geladen wurde oder?
Deshalb verstehe ich eben nicht, das die Felder Null sind.
Diese werden doch beim Laden des fxml initialisiert ?
 
Kann es daran liegen das du im FXML für das Bezugsdatum einen "DatePicker" verwendest. Im Controller aber einfach nur "Date"?
 
Ach stimmt ja, danke für den Tipp. Leider war dies nicht die Ursache der Exception.
Wenn ich die Felder nicht befülle, wird das Gui auch richtig angezeigt.
Sprich, das Laden hätte eigentlich geklappt.
Ich bin ratlos 🙁
 
Was hier vielleicht auch noch wichtig ist:

Die Methode "getPanel", welche das JfxPanel erzeugt und auf das Swing Panel packt (siehe Sourcecode) wird innerhalb eines TreeSelectionListener aufgerufen.

Die Methode "init()" dann etwas später innerhalb eines SwingWorker Threads (doInBackground).

Hat dies Einfluss auf die Kommunikation zwischen Controller und fxml ?
 

Zurück
Oben