How to connect FX controller with main app

The controller is binded in you fxml file or where you call the main view for the first time.

So you can use the fx:controller attribute in the xml or the FXMLLoader#setController() method from your launcher.


I think of an FXML file and its corresponding controller as a pair that manage the user interface. In larger applications you might have several such FXML-controller pairs that make up different parts of the user interface. Within each pair, the FXML file defines the layout of the UI, and the controller defines the logic (i.e. it typically processes user input, etc).

While you said you "know about FXMLLoader", if you understand that fully you would actually understand some of the other things you asked about, so:

The user interface defined by an FXML file and its controller is loaded into memory by an FXMLLoader. In the simplest setup, the controller is defined by a fx:controller attribute in the root element of the FXML file. When the load() method is called on the FXMLLoader, it:

  1. Loads the FXML file
  2. Creates an instance of the controller class specified by the fx:controller attribute, by calling its no-argument constructor
  3. Sets the value of any @FXML-annotated fields in the controller to the elements defined with matching fx:id attributes
  4. Registers any event handlers mapping to methods in the controller
  5. Calls the initialize() method on the controller, if there is one.

Notice the order of those events: the constructor is called before the @FXML-annotated fields are injected, but the initialize() method is called after. This means you can access (and configure) and @FXML-annotated fields in the initialize() method, but not in the constructor. It is quite common (at least in simple applications) not to define any constructor in the controller classes and just to use the default.

You can have as many FXML/controller pairs in your application as you need/want. Each FXML file should have its own controller class. You can load an FXML file as many times as you need if you want multiple instances of the UI it defines: each time the FXMLLoader will create a new controller instance for you that is associated with the UI element you loaded.

The Application subclass (you called it Main) represents the entire application. You should have only one such class per application and only one instance of it, which is created for you by the FX toolkit.

When you start a FX application (which I'll describe below), the FX toolkit is started. Then an instance of your Application subclass is created, and its init() method is called (if you don't define one, the default implementation does nothing). The FX Application Thread is then started and the Application subclass instance's start() method is called on that thread.

Your start() method should do pretty minimal work. Typically it will load your "main" fxml file, place the resulting UI in a scene, put the scene in the stage, and show the stage. All the logic will be handled by the controller for the FXML file, not by the Application subclass.

In more advanced applications, you might start some background services and/or create some data models in your init() method, and connect them with the controller in the start() method, but the ideas above are the basics.

The actual startup process can happen in a couple of ways. If you are using the standard Oracle JRE, then launching an Application subclass with

java Main

(where Main extends Application) will cause the process above to happen; in other words the FX toolkit is started, an instance of Main is created, its init() method is called, and it's start() method is called on the FX Application Thread.

Other environments (particularly IDEs) are not aware of the JavaFX startup process, and expect the class you are executing to have a public static void main(String[] args) method, like any standard Java application class. To support these environments, it is common for your Application subclass to define a main(...) method which simply calls launch(...) (a static method inherited from Application). The launch method forces the FX toolkit to start, etc. It can only be called once during any application lifetime.

So now you have something like:

package com.example ;

// imports...

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        // just load fxml file and display it in the stage:

        FXMLLoader loader = new FXMLLoader(getClass().getResource("mainUI.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    // main method to support non-JavaFX-aware environments:

    public static void main(String[] args) {
        // starts the FX toolkit, instantiates this class, 
        // and calls start(...) on the FX Application thread:
        launch(args); 
    }
}

Then you would have mainUI.fxml:

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

<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Label?>
<?import java.util.ArrayList?>

<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.MyController">
    <Label  fx:id="label1"/>
    <Label  fx:id="label2"/>
</VBox>

and the controller

package com.example ;

// imports...

public class MyController {

    @FXML
    private Label label1 ;
    @FXML
    private Label label2 ;

    // called by the FXML loader after the labels declared above are injected:
    public void initialize() {

        // do initialization and configuration work...

        // trivial example, could also be done directly in the fxml:
        label1.setText("Foo");
        label2.setText("Bar");
    }
}