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.
If you define a no-parameter
initialize()
method in your Controller and don't implement theInitializable
interface, then the FXML loader will still automatically invoke the initialize method.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 theInitializable
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.
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 ano-arg constructor
(OR you callFXMLLoader
'ssetControllerFactory()
and provide it with implementation of how the controller should be initialized) and is fully manageable byFXMLLoader
, then you go for thefx:controller
and set it in the fxml file itself.FXMLLoader
will load the controller and call itsinitialize()
if there is such method. This is the default way of linking a controller and fxml file.If you controller has a constructor with
at least 1 argument
or in the controller'sinitialize()
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 callsetController()
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:
- The
FXMLLoader
will read the FXML file, and, seeing thefx: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 theDemoController
bean from the application context. - The Spring application context will instantiate the
DemoController
. - The Spring application context will inject any dependencies into the
DemoController
instance. In this case, that means it will inject thedemoConfig
field. - The Spring application context will call any
@PostConstruct
-annotated methods. In your original version, this means it will call theinit()
method, which instantiates the text fields and sets their text to the values from thedemoConfig
. 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. - The
FXMLLoader
continues to parse the FXML file, instantiating the text fields defined in it. If theirfx:id
attributes match the names of eitherpublic
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 theinit()
method are immediately replaced by the text fields defined in the FXML file (so all the work done in theinit()
method is immediately lost). - 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 indemoConfig
.
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
How to Get the Current Date/Time in Java
How Does the String Class Override the + Operator
Java - Method Name Collision in Interface Implementation
Why Are Filenames in Java the Same as the Public Class Name
Regex for Matching Something If It Is Not Preceded by Something Else
What Does the Arrow Operator, '->', Do in Java
Any Reason to Prefer Getclass() Over Instanceof When Generating .Equals()
What Are the Differences Between the Different Saving Methods in Hibernate
What Is the Point of "Final Class" in Java
Returning from a Finally Block in Java
The Best Way to Print a Java 2D Array
How Will Java Lambda Functions Be Compiled
How to Parse a Mathematical Expression Given as a String and Return a Number
Comparator.Reversed() Does Not Compile Using Lambda
How to Make a Jar File That Includes Dll Files
Java: Insert Multiple Rows into MySQL with Preparedstatement