Implementing JavaFX FXML Action Events

JavaFX can implement event handling in several ways. This section explains the basics: implementation with tags and implementation with a controller.

Creating Event Handling with <fx:script>

FXML lets you build a GUI based on XML. But when actually creating one, you may wonder how event handling is written. Since XML separates the GUI from Java code, it needs a mechanism to connect to executable code in Java.

There are several ways to do this. First, let’s start with the simplest implementation using the <fx:script> tag.

FXML can actually include event handling code. It has the following form.

<fx:script>
function functionName(argument) {
    // describe processing
}
</fx script>

Define the processing to execute as a function like this. Then specify that function as an attribute on the control that uses it. For an action event, write it as follows.

onAction="functionName(argument);"

By writing an attribute on the control tag like this, the specified function is called when an action event occurs on that control.

You may wonder why function ... cannot be found in Java. In fact, this is not Java but a JavaScript function. In other words, with FXML, you can write event handling in JavaScript without using Java. If you want to quickly implement small processing, this is a very convenient approach.

Implementing Actions with <fx:script>

Now let’s actually create action event handling using the <fx:script> tag. We will reuse the previous project and modify the code.

Open the FXML file app.fxml and rewrite the source code as follows. You do not need to change anything else, such as the Java source code.

<?xml version="1.0" encoding="UTF-8"?>
 
<?language javascript?>
<?import java.lang.*?>
<?import java.net.URL ?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
 
<BorderPane xmlns="http://javafx.com/javafx"
        xmlns:fx="http://javafx.com/fxml">
    <fx:script>
    function doAction(event){
        var str = field1.getText();
        str = "당신은 쓴 글은 '" + str + "' 이것입니다.";
        label1.setText(str);
    }
    </fx:script>
    <stylesheets>
        <URL value="@app.css" />
    </stylesheets>
    <top>
        <Label fx:id="label1" text="This is FXML!" />
    </top>
    <center>
        <TextField fx:id="field1" />
    </center>
    <bottom>
        <Button onAction="doAction(event);" text="Click" />
    </bottom>
</BorderPane>

Now run it.

In this example, when the Button is clicked, text entered in the TextField is retrieved and a simple message is displayed in the Label. It is simple, but it shows the basics of action implementation.

There are several points here. Let’s summarize them in order.

<?language javascript?>

This tag has been added at the beginning. It specifies that the code written in <fx:script> is JavaScript. If you ask whether other languages can be used, the answer is yes.

If a scripting language that runs on the Java Virtual Machine, such as Groovy or Clojure, is available, you can specify it. However, JavaScript is the only one built into Java 8 as standard, so start by thinking of using this.

<fx:script>

The <fx:script> tag is included inside the <BorderPane> tag. In FXML, the Pane tag is written as the root tag, so the <fx:script> tag must be included inside it. This tag contains ordinary JavaScript.

Pay attention to the objects used here. Objects named label1 and text1 are used, and these correspond to the fx:id names of the <Label> and <TextField> written later.

Inside scripts written in <fx:script>, controls can be accessed as variables with the names specified by fx:id. Therefore, any controls you want to use must have fx:id assigned. As long as you follow this rule, you can freely use controls inside the script.

getText and setText

Inside the script, text is retrieved with getText and set with setText. These are methods we have already seen. They were used when working with control instances in Java source code.

Control objects used in scripts have the same methods as Java instances, so you can call them in the same way. There is nothing new to memorize.

External Script Files

Here, the script was written directly in the <fx:script> tag, but if the script becomes long, it is better to separate it into another file. In that case, specify the source attribute. For example:

<fx:script source="script.js"/>

Writing it this way loads script.js from the same location as the FXML file. If the script grows long, try separating it into another file with source.

Using a Controller with FXML

JavaScript is easy and convenient, but many people naturally want to write concrete processing in Java. In that case, define the processing in a Java class and assign it to controls in FXML.

Such a class that performs concrete event handling is generally called a controller. A controller class is basically a POJO, a simple class that does not have to extend anything.

Specify the controller on the FXML Pane class with the fx:controller attribute. Then that class is configured as the controller, and methods in the class can be specified directly as event handling attributes such as doAction.

Let’s create a controller. First, rewrite app.fxml as follows.

<?xml version="1.0" encoding="UTF-8"?>
 
<?language javascript?>
<?import java.lang.*?>
<?import java.net.URL ?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
 
<BorderPane xmlns="http://javafx.com/javafx"
        xmlns:fx="http://javafx.com/fxml"
        fx:controller="com.devkuma.javafx.AppController">
    <stylesheets>
        <URL value="@app.css" />
    </stylesheets>
    <top>
        <Label fx:id="label1" text="This is FXML!" />
    </top>
    <center>
        <TextField fx:id="field1" />
    </center>
    <bottom>
        <Button fx:id="btn1" onAction="#doAction" text="Click" />
    </bottom>
</BorderPane>

This time, it is written to use a controller class named AppController. Looking at the Pane tag:

<BorderPane xmlns="http://javafx.com/javafx" 
    xmlns:fx="http://javafx.com/fxml"
    fx:controller="com.devkuma.javafx.AppController">

With fx:controller="com.devkuma.javafx.AppController", the AppController class is specified as the controller.

Also, looking at the <Button> tag, you can see that the onAction attribute has been subtly changed.

<Button onAction="#doAction" text="Click" />

Controller methods are specified in the form #methodName. Then the doAction method of the AppController class is set as the action event handler for this Button.

Implementing the Controller

Now create the controller class. This time, create a file named AppController.java in the com.devkuma.javafx package and write the following source code.

package com.devkuma.javafx;
 
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
 
public class AppController {
    @FXML Label label1;
    @FXML TextField field1;
     
    @FXML
    protected void doAction(ActionEvent ev){
        String str = field1.getText();
        str = "당신은 쓴 글은 '" + str + "' 이것입니다.";
        label1.setText(str);
    }
}

This produces the same effect as the earlier version using the <fx:script> tag.

Let’s look at the main points in the source code.

@FXML Label label1;
@FXML TextField field1;

The class uses Label and TextField as instance fields. However, there is no code that assigns instances to these variables. By adding the @FXML annotation, instances with the same fx:id names in FXML are bound to these fields.

@FXML
protected void doAction (ActionEvent ev) {...}

The action method is also bound because it has the @FXML annotation. Note that the method should use the protected access modifier. It also receives an instance of an Event class as an argument. For an action event, an ActionEvent instance is passed.

There are no other special points to watch for. Instances are assigned to the fields marked with @FXML, so you can write normal Java code to manipulate them. Now you can write the processing comfortably in Java.

Implementing Action Events with setOnAction

Implementing action events with a controller is very simple, but it requires writing onAction in FXML. In AWT and Swing, it was basic to implement event handling in Java code.

When all processing was written in the Application class without using FXML, action events could be registered with the setOnAction method. Even when using FXML and a controller, this is the same. The difference is that setOnAction is called on the controller side rather than in the Application class.

Let’s implement it this way too. First, remove the onAction attribute from the FXML <Button> tag, and change the controller class source code as follows.

package com.devkuma.javafx;
 
import java.net.URL;
import java.util.ResourceBundle;
 
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
 
public class AppController implements Initializable {
    @FXML Label label1;
    @FXML TextField field1;
    @FXML Button btn1;
     
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        btn1.setOnAction((AtionEvent)->{
            String str = field1.getText();
            str = "당신은 쓴 글은 '" + str + "' 이것입니다.";
            label1.setText(str);
        });
    }
 
}

Run it and click the button. You can confirm that the processing is executed correctly.

In this example, the controller class definition has changed slightly from the previous controller. It now implements the Initializable interface from javafx.fxml. This adds functionality related to FXML initialization processing.

The initialize method has been added to the class. Write FXML-related initialization processing here. When this method is called, instance fields with the @FXML annotation have already been created and assigned, so you can call setOnAction on the Button directly and assign an action event.

With this, you can implement action events based on FXML. Try running various kinds of processing and checking how they behave.