Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javafx - Can application class be the controller class

Tags:

javafx

fxml

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)?

like image 736
Dave S. Avatar asked Oct 23 '15 13:10

Dave S.


People also ask

What is JavaFX Application class?

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.

Which interface should be used with controller class of JavaFX Application?

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.

Where in a FXML file do I need to specify a controller class?

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.


2 Answers

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:

  • Read the root FXML element.
    • If the root FXML element has a fx:controller attribute, then
      • If a controller already exists, throw an exception, otherwise create an instance of the specified class1 and set that as the controller
  • Continue parsing the FXML file. If elements have a 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.
  • Invoke 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).

like image 51
James_D Avatar answered Nov 07 '22 07:11

James_D


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();
like image 43
creativ Avatar answered Nov 07 '22 06:11

creativ