Working with FXML Views

FXML (FX Markup Language) is an XML-based language for defining JavaFX user interfaces declaratively. Instead of creating UI elements in Java code, you define them in FXML files, which are then loaded at runtime. This cookbook section explains how FXML works in the Jukebox project.

What You'll Learn:
  • FXML file structure and syntax
  • Connecting FXML files to controller classes
  • Loading FXML with FXMLLoader
  • Basic FXML elements and attributes
  • The view factory pattern used in Jukebox

FXML File Structure

Let's look at a simple FXML file from the Jukebox project. The main application window is defined in application-window.fxml:

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>

<!--
    Application Window - Main Container FXML
    
    This is the root FXML file for the application.
-->

<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" 
     minHeight="-Infinity" minWidth="-Infinity" prefHeight="778.0" 
     prefWidth="621.0" stylesheets="@/styles/application-window.css"
     xmlns="http://javafx.com/javafx/25" 
     xmlns:fx="http://javafx.com/fxml/1" 
     fx:controller="jukebox.controller.ApplicationWindowController">
     
    <children>
        <!-- Header bar -->
        <HBox alignment="CENTER_RIGHT">
            <children>
                <Label alignment="CENTER" prefWidth="467.0" text="JUKEBOX" HBox.hgrow="ALWAYS" />
                <Button mnemonicParsing="false" onAction="#onHomeButtonClicked" text="GO HOME" />
            </children>
        </HBox>
        
        <!-- Main container for views -->
        <VBox fx:id="container" prefHeight="200.0" prefWidth="100.0" VBox.vgrow="ALWAYS" />
    </children>
    
    <padding>
        <Insets left="8.0" right="8.0" top="10.0" />
    </padding>
</VBox>

XML Declaration

Every FXML file starts with the standard XML declaration:

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

Imports

FXML files use a special import syntax to import JavaFX classes:

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import javafx.geometry.Insets?>
FXML Imports:
  • Use <?import class.name?> syntax for each class you want to use
  • These are not Java imports - they're FXML-specific
  • They tell the FXMLLoader which classes are referenced in the file

Root Element

The root element defines the top-level container for your UI. In this case, it's a VBox (vertical box layout). Notice the important attributes:

<VBox alignment="TOP_CENTER" prefHeight="778.0" prefWidth="621.0"
     stylesheets="@/styles/application-window.css"
     xmlns="http://javafx.com/javafx/25"
     xmlns:fx="http://javafx.com/fxml/1"
     fx:controller="jukebox.controller.ApplicationWindowController">

Child Elements

FXML files use a hierarchical structure. The root VBox contains <children> which contains the child nodes:

<VBox ...>
    <children>
        <HBox>...</HBox>
        <VBox fx:id="container" ... />
    </children>
</VBox>
Element Hierarchy:
  • Every FXML file has exactly one root element
  • Child elements are contained within <children> tags
  • The hierarchy matches the JavaFX scene graph structure
  • Each element can have its own properties and children

Special FXML Attributes

FXML introduces some special attributes that start with fx::

Connecting Controller to FXML

The fx:controller attribute tells FXMLLoader which controller class to use for this FXML file. The controller class must have public fields with the same names as the fx:id attributes, or use @FXML annotations.

In ApplicationWindowController.java:

public class ApplicationWindowController {
    
    // FXML INJECTED ELEMENTS
    // This field matches fx:id="container" in the FXML file
    public VBox container;
    
    // ... rest of the controller
}
Controller Injection:
  • When FXMLLoader loads the FXML file, it creates an instance of the controller class
  • It then sets the controller's fields to match the nodes with corresponding fx:id values
  • Fields can be public, or private with @FXML annotation

Loading FXML with FXMLLoader

In the Jukebox project, FXML files are loaded using the FXMLLoader class. Each controller has a static createViewInstance() method that handles the loading:

// In ApplicationWindowController.java
public static Region createViewInstance() {
    // Create an FXMLLoader to load the view definition from file
    FXMLLoader loader = new FXMLLoader(
        ApplicationWindowController.class.getResource(
            "/fxml/application-window.fxml")
    );
    try {
        // Load the FXML file - this creates all the UI elements
        Parent root = loader.load();
        
        // Get the controller instance created by the FXMLLoader
        Jukebox.applicationWindowController = loader.getController();
        
        // Return the root region to be used as the scene's root
        return (Region) root;
    } catch (IOException e) {
        // If we can't load the FXML file, throw a runtime exception
        throw new RuntimeException(e);
    }
}
FXMLLoader Steps:
  1. Create an FXMLLoader instance with the path to the FXML file
  2. Call loader.load() - this loads the FXML and creates all UI nodes
  3. The loader automatically creates an instance of the controller class (from fx:controller)
  4. It injects all fx:id nodes into the controller's matching fields
  5. Call loader.getController() to get the controller instance
Important: Resource Path

Notice the path starts with a forward slash: /fxml/application-window.fxml. This is a classpath-relative path. The FXML files are stored in src/resources/fxml/, which is on the classpath, so they're accessible with this path.

Event Handling in FXML

FXML files can directly specify event handlers. In the application window FXML, the button has an onAction attribute:

<Button mnemonicParsing="false" onAction="#onHomeButtonClicked" text="GO HOME" />

This connects the button's action event to a method in the controller:

// In ApplicationWindowController.java
public void onHomeButtonClicked(ActionEvent actionEvent) {
    displayHomeView();
}
Event Handler Naming:
  • The # prefix tells FXMLLoader this is a method reference
  • The method name in the controller must match exactly
  • The method must have the correct signature (e.g., void onActionName(ActionEvent))
  • Common event types: ActionEvent, MouseEvent, KeyEvent

The View Factory Pattern

In the Jukebox project, each view/controller pair uses a factory pattern with a static createViewInstance() method. This provides several benefits:

Benefits of the Factory Pattern:
  • Consistent Creation: All views are created the same way
  • Centralized Access: Controllers are stored in static fields for easy access
  • Lazy Initialization: Views are only created when first needed
  • Encapsulation: The creation logic is hidden from the rest of the application

Example from ApplicationWindowController:

public void initialize() {
    // Create and cache all views
    chooseAlbumView = ChooseAlbumController.createViewInstance();
    homeScreenView = HomeScreenController.createViewInstance();
    playAlbumView = PlayAlbumController.createViewInstance();
    adminPanelView = AdminPanelController.createViewInstance();
    
    // Display the home view
    displayHomeView();
}

Creating Your Own FXML View

Let's create a simple FXML file for a new view:

  • Step 1: Create the FXML file

    Create src/resources/fxml/my-view.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.geometry.Insets?>
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.Label?>
    <?import javafx.scene.layout.VBox?>
    
    <VBox alignment="CENTER" spacing="20" 
         xmlns="http://javafx.com/javafx/25" 
         xmlns:fx="http://javafx.com/fxml/1"
         fx:controller="myapp.MyController">
        
        <padding>
            <Insets bottom="20" left="20" right="20" top="20"/>
        </padding>
        
        <children>
            <Label fx:id="welcomeLabel" text="Welcome to My View!" />
            <Button fx:id="actionButton" onAction="#handleButtonClick" text="Click Me" />
        </children>
    </VBox>
  • Step 2: Create the Controller

    Create src/java/myapp/MyController.java:

    package myapp;
    
    import javafx.event.ActionEvent;
    import javafx.scene.control.Label;
    import javafx.scene.layout.Region;
    import javafx.fxml.FXMLLoader;
    import java.io.IOException;
    
    public class MyController {
        // Injected from FXML
        public Label welcomeLabel;
        public javafx.scene.control.Button actionButton;
        
        public void handleButtonClick(ActionEvent event) {
            welcomeLabel.setText("Button Clicked!");
        }
        
        public static Region createViewInstance() {
            FXMLLoader loader = new FXMLLoader(
                MyController.class.getResource("/fxml/my-view.fxml")
            );
            try {
                return loader.load();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
  • Step 3: Use the View

    From your main application or another controller:

    Region myView = MyController.createViewInstance();
    // Add to your scene or container
    container.getChildren().add(myView);
  • Try This:

    1. Open application-window.fxml and look at the structure
    2. Find the fx:controller attribute and verify it matches ApplicationWindowController
    3. Find a node with an fx:id and then find the matching field in the controller class
    4. Find a button with onAction and find the corresponding method in the controller

    Common FXML Patterns in Jukebox

    1. Layout Containers

    Jukebox uses various layout containers:

    2. UI Controls

    Common controls used in the Jukebox FXML files:

    3. Stylesheets

    FXML files can reference CSS stylesheets using the stylesheets attribute:

    <VBox stylesheets="@/styles/application-window.css" ...>

    Key Takeaways

    Related Cookbook Sections