I'm currently teaching myself JavaFX, and I've taken a simple example program that's hardcoded the view and am turning it into one that uses FXML (mostly so I can use SceneBuilder for building UIs). Rather than writing a separate controller class, I'm using the application class (so there's 1 Java file and 1 FXML file). I'm not using an initialize()
method as it's a linear flow (display the UI, populate the fields, wait for input). The view pops up, but then the app errors out as none of the controls are mapped to the appropriate variables (so for @FXML TableView<...> table
, table
is null
).
However, I put in an initialize()
method for debugging, the controls are injected while in initialize()
, and then return to null when initialize()
exits.
So the question is, does JavaFX instantiate a new instance of the application class as a separate controller class? This would explain why the variable are going out of scope. Or is it something else (e.g. the controls are injected only when being called back from JavaFX actions)?
The entry point for JavaFX applications is the Application class. The JavaFX runtime does the following, in order, whenever an application is launched: Constructs an instance of the specified Application class. Calls the init() method. Calls the start(javafx.stage.Stage) method.
JavaFX controller works based on MVC(Model-View-Controller) JavaFX MVC can be achieved by FXML (EFF-ects eXtended Markup Language). FXML is an XML based language used to develop the graphical user interfaces for JavaFX applications as in the HTML.
It is also possible to assign the controller class directly from the FXML file. In order to do that, you should first open your FXML file and add the following code on the first line of the file right after declarations. Example: Since my controller is inside the package name “view”, my fx: controller = “view.
The default behavior of the FXMLLoader
is to create a new instance of the controller class and use that instance as the controller.
Specifically, the FXMLLoader
does something like:
fx:controller
attribute, then
fx:id
attribute, and a controller exists (by any mechanism), inject those fields into the controller. Similarly register event handlers as calls to methods in the controller instance.initialize()
on the controller, if a controller exists and it has such a method.So, the question you asked:
Can application class be the controller class
Yes, but it's probably a terrible idea. If you simply specify the Application
subclass as the controller class using fx:controller
, then a second instance of the Application
subclass is created, @FXML
-annotated fields are injected on that second instance, and the initialize()
method is invoked on that second instance. Obviously, the @FXML
-fields are never initialized on the instance on which start(...)
is invoked, and the initialize()
method is never invoked on that instance.
The question you probably meant is:
Can the application class instance created at launch be used as the controller?
The answer to this is also yes, and, aside from very small demo programs you intend to immediately discard, it's also probably a very bad idea. You would do this by
public class MyApp extends Application {
@FXML
private Node someNode ;
public void initialize() {
// do something with someNode
}
@Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/path/to/fxml/file.fxml"));
loader.setController(this);
Parent root = loader.load();
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Note that to use this code, your FXML file must not have a fx:controller
attribute.
The problem with this is that you have no separation and no flexibility. (E.g. if you create a second instance of the view defined in your FXML file somewhere, you end up with a second Application
subclass instance, which is at best counterintuitive (one application with two Application
instances...).)
So I would advocate using a separate class for the controller in basically every case. The Application
subclass should contain minimal code and should be used only for starting the application.
1 This step is actually a little more complex. If a class is specified in the fx:controller
attribute, and no controller already exists, the FXMLLoader
checks for a controllerFactory
. If one exists, then the controller is set as the result of passing the specified Class
to the controllerFactory
's call()
method, otherwise it is created by calling newInstance()
on the specified class (effectively calling its no-argument constructor).
If you have defined your application class to be the controller in the FXML file, JavaFX will, if I remember correctly, create a new instance of your application class and use the new instance as a controller. Thus, your existing application class still has null for the table.
You can however define the controller programmatically in your application class to use your own instance:
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("example.fxml"));
fxmlLoader.setController(this);
Parent root = (Parent)fxmlLoader.load();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With