How can a textfield from fxml file be updated by setText in java file?

22.9k views Asked by At

I am looking to update text in a textfield based on some value. In order to make this sample simpler I have made my program smaller. The problem seems to be when I put top.setText("This is my new Text");

I looked at this: how to change the text of TextField in java fx 2

but the answer does not seem to make sense. I don't know why you'd initialize a textfield that has already been implemented. Regardless it did not work.

I have also looked at: NullPointerException (JavaFX Label.setText())

This seems to be the closest to what I think is the issue, but when I did the following I get an error. Just for clarity this is in the JavaFXApplication5 class.

try {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("FXML.fxml")
        );
        FXMLLoader.setController(this); // non-static method setController(Object) 
                                        // can not be referenced from static context ERROR****
        Parent root = (Parent) loader.load();
        /*Parent root = FXMLLoader.load(getClass().getResource("FXML.fxml"));

        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("java code");
        stage.show();
        */

    }

From reading on the internet I wondered if there was a race condition: http://gotoanswer.stanford.edu/nullpointerexception_in_javafx_initialize_method-8807679/

So I tried:

Platform.runLater(new Runnable() {
  @Override public void run() {
    top.setText("This is my new Text");
  }
});

But that did not work. I know that it can be set in Scene Builder but I need a way to dynamically change it based on values from another class. I can figure out how to do that part if I can just figure out how to set it to begin with. Hopefully this explains enough to get some help.

    FXMLController Class:
    public class FXMLController implements Initializable {

        @FXML private TextField top;

        public FXMLController() {
            System.out.println("Hi");
            top.setText("This is my new Text"); // This breaks the program *********
        }
        @Override
        public void initialize(URL url, ResourceBundle rb) {
        } 
    }

    FXML.fxml class:
    <?xml version="1.0" encoding="UTF-8"?>

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

    <AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="javafxapplication5.FXMLController">
       <children>
          <TextField fx:id="top" layoutX="171.0" layoutY="68.0" />
       </children>
    </AnchorPane>

JavaFXApplication5 class: // main class
public class JavaFXApplication5 extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("FXML.fxml"));
            Scene scene = new Scene(root);
            stage.setScene(scene);
            stage.setTitle("java code");
            stage.show();
        }
        catch (Exception ex) {
            Logger.getLogger(JavaFXApplication5.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public static void main(String[] args) {
        launch(args);
    }  
}
1

There are 1 answers

3
José Pereda On BEST ANSWER

You can't use a constructor on your controller class (FXMLController), since it will be initilized by the FXMLLoader.

And you are right, the first link has a wrong answer, since the textfield will be initialized (because of the @FXML annotation) in this process.

So for starters, you can add some text to the textfield inside initialize, as it will be loaded from the beginning by the loader, and top will be already instantiated.

public class FXMLController implements Initializable {

    @FXML private TextField top;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        top.setText("This is my first Text");
    } 
}

Try this first, with your posted version of JavaFXApplication5, and check that works.

There are many ways to set the content on the field, but if you need to modify the text field from another class, just add a public method for that:

public class FXMLController implements Initializable {

    @FXML private TextField top;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        top.setText("This is my new Text");
    } 

    public void setTopText(String text) {
        // set text from another class
        top.setText(text);
    } 

}

As an example, you could get an instance of your controller in your main class, and use it to pass the content to the text field, after the stage is shown. This will override the previous content.

@Override
public void start(Stage stage) throws IOException {
    FXMLLoader loader = new FXMLLoader(getClass().getResource("FXML.fxml"));
    Parent root = loader.load();
    FXMLController controller = (FXMLController)loader.getController();

    Scene scene = new Scene(root);

    stage.setTitle("Java code");
    stage.setScene(scene);
    stage.show();

    controller.setTopText("New Text");
}