Yin
Yin

Reputation: 415

java.lang.NullPointerException in handling with FXML inside another FXML in Javafx

I have been trying to figure out how to deal with a FXML file in another FXML file. But I have an exception. :( This is my inner FXML (NewInside.fxml in view package):

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

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


<AnchorPane prefHeight="305.0" prefWidth="360.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="controller.NewInsideController">
   <children>
      <Button fx:id="newBtn" layoutX="30.0" layoutY="202.0" mnemonicParsing="false" onAction="#btnClick" text="Button" />
      <TextField fx:id="newTxt" layoutX="30.0" layoutY="134.0" prefHeight="25.0" prefWidth="280.0" />
      <Label fx:id="newLbl" layoutX="30.0" layoutY="62.0" prefHeight="17.0" prefWidth="280.0" />
   </children>
</AnchorPane>

This is its controller (NewInsideController.java in controller package):

package controller;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class NewInsideController implements Initializable{

    @FXML public static Label newLbl;
    @FXML public static TextField newTxt;
    @FXML public static Button newBtn;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }

    @FXML
    private void btnClick(ActionEvent e){
        System.out.println("Button is clicked!");
        newLbl.setText(newTxt.getText());
    }

}

And this is outer FXML (New.fxml in view package):

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

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


<AnchorPane prefHeight="380.0" prefWidth="529.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="controller.NewController">
   <children>
      <fx:include source="NewInside.fxml" />
   </children>
</AnchorPane>

This is its controller (NewController.java in controller package):

package controller;

import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.Initializable;

public class NewController implements Initializable {

    /*@FXML private Label newLbl;
    @FXML private Button newBtn;
    @FXML private TextField newTxt;
    */

    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }

    /*@FXML
    public void btnClick(ActionEvent e){
        newLbl.setText(newTxt.getText());
    }*/

}

Finally, this is the Main.java in application package:

package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("/view/New.fxml"));
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

When I clicked the button, it gives me very long exception: "Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException.... ..... Caused by: java.lang.NullPointerException at controller.NewInsideController.btnClick(NewInsideController.java:27) ... 57 more"

Please help me! :(

Upvotes: 0

Views: 11092

Answers (1)

Jos&#233; Pereda
Jos&#233; Pereda

Reputation: 45486

As @Inge suggests, using the static field is not the right way to do it. Also, instead of public, you should use private. If you need to expose your controls, use their properties.

You can read why here:

Just change this in your NewInsideController:

@FXML private Label newLbl;
@FXML private TextField newTxt;
@FXML private Button newBtn;

EDIT

If you want to have access to your controls from other classes, instead of adding getters/setters to expose directly the controls, it is better just exposing their properties.

For instance, to have access to the text of these controls you could add:

public StringProperty newLblText() { 
    return newLbl.textProperty();
}
public StringProperty newTxtText() { 
    return newTxt.textProperty();
}
public StringProperty newBtnText() { 
    return newBtn.textProperty();
}

This will allow you binding (or listening to changes), getting/setting the text property from any of them from the outside of your controller.

To get a valid instance of NewInsideController from NewController, follow this link. First modify New.fxml:

<fx:include fx:id="newInside" source="NewInside.fxml" />

and now you can get an instance, and add some listeners with those properties:

public class NewController implements Initializable {

    @FXML
    private Parent newInside;
    @FXML
    private NewInsideController newInsideController;

    @Override
    public void initialize(URL url, ResourceBundle rb) {

        newInsideController.newBtnText().addListener(
            (obs,s,s1)->System.out.println("nweBtn Text;" +s1));
        if(newInsideController.newLblText().get().isEmpty()){
            newInsideController.newLblText().set("Text for the textfield");
        }
        newInsideController.newTxtText().addListener(
           (obs,s,s1)->System.out.println("s1 "+s1));
    }    
}

Upvotes: 1

Related Questions