Javafx Fxml Controller - Constructor VS Initialize Method

JavaFX FXML controller - constructor vs initialize method

In a few words: The constructor is called first, then any @FXML annotated fields are populated, then initialize() is called.

This means the constructor does not have access to @FXML fields referring to components defined in the .fxml file, while initialize() does have access to them.

Quoting from the Introduction to FXML:

[...] the controller can define an initialize() method, which will be called once on an implementing controller when the contents of its associated document have been completely loaded [...] This allows the implementing class to perform any necessary post-processing on the content.

JavaFX FXML constructor and initialize method

All public fields and non-public fields with the @FXML annotation with names which match an fx:id are initialized by the FXMLLoader in your assigned controller class (using FXMLLoader.setController(Object)) when the FXMLLoader.load() method is invoked.

So it can be assumed (since it is not included in your question) that you have a ListView in your FXML file Main.fxml with fx:id="historyLV". This is why you do not get NullPointerException.

what does initialize() mean in javafx?

The initialize() method is used to initialize any controls, especially when it is something that can't be done from the FXML. An example of this is setCellFactory() and setCellValueFactory(). If you have nothing to initialize, then you do not need to put an initialize() method in your controller class.

Some people thought that they could initialize in the controller class constructor, but that is the wrong place to initialize. This is because FXML injection only occurs after the controller class has been instantiated. FXML injection occurs just before initialize() is being called, which makes it safe to perform initialization in initialize(). Initializing in constructor will give you NullPointerException, if you used the reference of an injected control (e.g. @FXML private Button button).

In JavaFX 2.0, controller classes were required to implement the Initializable interface if the controller has anything to initialize. If you added initialize() without implementing the interface, no initialization will occur.

This was changed in JavaFX 8.0. Controller classes will no longer need to implement Initializable interface, but you may still choose to do so. The only thing to take note is that you need to add the @FXML annotation if the initialize() is non-public. If the initialize() method is public, then you can just skip the annotation.

I would recommend keeping the initialize() method private (or protected, which is rarely useful). Keeping initialize() private makes it less likely for initialization to occur more than once, which could potentially cause unexpected behavior.

Minor Update

In case you are wondering how initialize() gets called when it does not implement the Initializable interface, it is called via the use of Java Reflection. That is why you need to use the exact method name.

Using initialize method in a controller in FXML?

You must set the controller in FXML loading

There are two ways to do it:

1º way: Set the controller in FXMLLoader class. Instead of do that

FXMLLoader.load(getClass().getResource("/view/WindowsView.fxml"));

Do this

FXMLLoader loader = new FXMLLoader();
loader.setController(new Controller());
loader.setLocation(getClass().getResource("/view/WindowsView.fxml"));
root = loader.load();

2º way: Set controller in FXML

WindowsView.fxml

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

<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.TextField?>

<AnchorPane xmlns:fx="http://javafx.com/fxml/1" fx:controller="view.Controller">
<Label text="This is my example in StackOverflow"/>
</AnchorPane>

JavaFX FXML Controller initialize method not invoked

The issue was that the Controller's wasn't being initialized as the asker of the question expected.

The operation of the FXMLLoader in Java 8 is a little weird.

  1. If you define a no-parameter initialize() method in your Controller and don't implement the Initializable interface, then the FXML loader will still automatically invoke the initialize method.

  2. If instead, you define a parameterized public void initialize(URL url, ResourceBundle rb) method in your Controller, then the FXML loader will not automatically invoke the initialize method unless your controller also implements the Initializable interface.

As the code in the question was using a parameterized initialize method and not also implementing Initializable, the initialization was not occurring.


The parameterized initialize method should be replaced in FXML by injection of the parameters via @FXML annotation, for more details see:

  • What is "automatic injection of location and resources properties into the controller" in JavaFX?

    @FXML 
    private ResourceBundle resources;

    @FXML
    private URL location;

Difference between initialized controllers and FXML linked controllers?

There is no functional difference between the two methods of setting a controller for an fxml file. However, in terms of when to use which there is a slight distinction.

  1. If your controller doesn't need any external objects to initialize its state before calling its own initialize(), in other words your controller class has a no-arg constructor (OR you call FXMLLoader's setControllerFactory() and provide it with implementation of how the controller should be initialized) and is fully manageable by FXMLLoader, then you go for the fx:controller and set it in the fxml file itself. FXMLLoader will load the controller and call its initialize() if there is such method. This is the default way of linking a controller and fxml file.

  2. If you controller has a constructor with at least 1 argument or in the controller's initialize() it requires access to fields which must be initialized externally (not within the controller class), then you manually manage the controller. You create an instance of it, like with any other Java class, initialize what is required and only then call setController() to link your controller with the fxml file. This technique is typically used with custom controllers

For more details please have a look at this: http://docs.oracle.com/javase/8/javafx/api/javafx/fxml/doc-files/introduction_to_fxml.html#custom_components

How to pass parameter to a constructor of fxml controller class in javafx?

you can not directly pass parameter into the FXMLLoader Controller constructor, instead you get the controller from the FXMLLoader instance and calling a method on the controller to initialize it with parameter.

FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = loader.load();
ViewController viewController = loader.<ViewController>getController();
viewController.initParameter(parameter);

Javafx. Controller Initialize method and spring @PostConstruct cannot use together

Your init() method is completely redundant, and should be deleted. The correct controller class should look like this:

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class DemoController implements Initializable
{
@FXML
private TextField platformName;

@FXML
private TextField platformVersion;

@FXML
private TextField deviceName;

@FXML
private TextField appActivity;

@FXML
private TextField appPackage;

@Autowired
private DemoConfig demoConfig;

@Override
public void initialize(URL url, ResourceBundle resourceBundle)
{
platformName.setText(demoConfig.getPlatformName());

platformVersion.setText(demoConfig.getPlatformVersion());

deviceName.setText(demoConfig.getDeviceName());

appActivity.setText(demoConfig.getAppActivity());

appPackage.setText(demoConfig.getAppPackage());
}

}

The reason for this is explained by @slaw in a comment on the question. For completeness, I'll repeat that information here:

Assuming everything is set up correctly in the code you didn't post, when you call load() on your FXMLLoader, the following will occur:

  1. The FXMLLoader will read the FXML file, and, seeing the fx:controller attribute, will get an instance of the specified class using its controller factory. Since you've presumably configured the controller factory to use the Spring Application Context to supply the controller, it will request the DemoController bean from the application context.
  2. The Spring application context will instantiate the DemoController.
  3. The Spring application context will inject any dependencies into the DemoController instance. In this case, that means it will inject the demoConfig field.
  4. The Spring application context will call any @PostConstruct-annotated methods. In your original version, this means it will call the init() method, which instantiates the text fields and sets their text to the values from the demoConfig. Since these text fields are not the ones defined in the FXML file, they are not part of the UI, and this has no visible effect. In the correct version above, there are no @PostConstruct-annotated methods, and this step does nothing.
  5. The FXMLLoader continues to parse the FXML file, instantiating the text fields defined in it. If their fx:id attributes match the names of either public fields, or (better) @FXML-annotated non-public fields, these fields will be initialized by the text fields defined as part of parsing the FXML file. (These are the text fields that are part of the UI.). So, in your original version, the text fields initialized in the init() method are immediately replaced by the text fields defined in the FXML file (so all the work done in the init() method is immediately lost).
  6. The initialize() method is invoked on the controller. The definition of this method in both your version and the version I posted above now sets the text of the correct text fields (i.e., the ones defined in the FXML file that are part of the UI) to the values in demoConfig.

As you can see, everything you do in your init() method is redundant (because is sets the text of text fields that are never displayed in the UI), and is immediately undone anyway (because the FXMLLoader immediately replaces the text fields it defines with new text fields).

Consequently, you should simply delete the init() method as shown above. If that fails to do what you want, then there are other errors in code you haven't posted.

Note that I also changed the scope of the controller to be prototype. If you were to load the FXML file more than once, you would get a new set of text fields each time, and you would need a new controller instance specifically for that set of controls. When using Spring with JavaFX, all controllers should always be prototypes.



Related Topics



Leave a reply



Submit