Using JavaFX Action Events
This page first explains event handling for button clicks, known as action events, by using the most basic GUI elements: an input field and a push button. It also explains an implementation that uses Java 8 lambda expressions.
TextField and Button
The basics of a GUI are, after all, user input and command execution. The most familiar elements for these in ordinary applications are input fields and push buttons. This time, let’s use those.
About TextField
First, the input field. The most commonly used one is for entering a single line of text. This is provided as a class named TextField in the javafx.scene.control package. Create an instance of this class as follows.
new TextField()
new TextField(initial value)
Creating it with no arguments is the basic form. If you specify a String argument, that text is set as the initial value of the field. The entered text can be retrieved and changed as follows.
Getting Text
String variable = textField.getText();
Changing Text
textField.setText(text);
TextField also supports “prompt text.” This displays a gray message when nothing is written in the field and the field is not selected. You have probably seen faint guide text such as “Enter name” or “Enter address” in input fields. It is the same thing.
Setting Prompt Text
textField.setPromptText(text);
About Button
A push button is provided as a class named Button in the javafx.scene.control package. It can also be instantiated as follows.
new Button()
new Button(display text)
If you specify a String as the text argument and create it with new, that text is displayed on the button. When using a push button, text such as “Send” or “Click” is usually displayed on the button, so entering this text is the basic style. Also, as with TextField, you can manipulate the displayed text with getText and setText, so you can create a button with new Button() and set the display text later.
The source code example below is an application that uses GUI elements such as Label, TextField, and Button. Each instance is created and then placed in a BorderPane for display.
package com.devkuma.javafx;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
Label label;
TextField field;
Button button;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
label = new Label("This is JavaFX!");
field = new TextField();
button = new Button("Click");
BorderPane pane = new BorderPane();
pane.setTop(label);
pane.setCenter(field);
pane.setBottom(button);
Scene scene = new Scene(pane, 320, 120);
stage.setScene(scene);
stage.show();
}
}
setOnAction and EventHandler
A push button, or Button, is not used only for display. It is used so that some processing is performed when it is clicked. This processing is executed by using an “event” included in Button.
An event is like a signal that occurs when various operations are performed and the program state changes. Each control, such as TextField or Button, has defined events, and it includes methods that perform the functions needed to process those events.
The event that occurs when a Button control is clicked is called an “action event.” This is the event used for processing when that control is used in its most general form. For example, for a push button, the basic operation is clicking. When such an operation is performed, an action event occurs.
An action event can be set by using the following method included in Button.
button.setOnAction("EventHandler");
Processing for generated events uses the EventHandler interface in the javafx.event package.
EventHandler Structure
EventHandler already provides a method that is called when an event occurs. By overriding this method, you can implement processing for when the event occurs. It can be used in the following form.
public void handle (Event e) {
// Prepare processing here.
}
When using it, you will usually prepare a class that implements EventHandler, or create an instance with new as an anonymous class and pass it as an argument to the method.
EventHandler and handleEvent
EventHandler is a simple interface that contains only one method. You can perform the required processing simply by defining the public void handle(Event e) method. However, it is rarely used exactly as-is.
The handle method in EventHandler can receive the event that occurred as an instance argument of a class named Event. This Event has many subclasses for each event type, and EventHandler is designed so that you can collectively specify which Event subclass for which event is passed.
For example, when creating an EventHandler using an anonymous class, you would write it as follows.
new EventHandler() {
@Override
public void handle (Event e) {
// Write processing code here.
}
}
For an action event, a subclass of Event named ActionEvent is passed as the argument. It is common to write setOnAction as follows.
new EventHandler<ActionEvent>() {
public void handle (ActionEvent e) {
// Write processing code here.
}
}
In the usual form, specify ActionEvent and write the handle method so that an ActionEvent is passed as its argument. This makes it possible to create an EventHandler that processes action events.
Now let’s create an event processing example that specifies an EventHandler for a Button.
package com.devkuma.javafx;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
Label label;
TextField field;
Button button;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
label = new Label("This is JavaFX!");
field = new TextField();
button = new Button("Click");
// Specify action event processing.
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
String msg = "you typed: " + field.getText();
label.setText(msg);
}
});
BorderPane pane = new BorderPane();
pane.setTop(label);
pane.setCenter(field);
pane.setBottom(button);
Scene scene = new Scene(pane, 320, 120);
stage.setScene(scene);
stage.show();
}
}
This example retrieves the text entered in the upper field when the displayed button is clicked and shows the message in the label. You can see that new EventHandler<ActionEvent>() is set in setOnAction.
Make It More Concise with Lambda Expressions
Now the action event for when the Button is clicked is complete. However, writing it with an anonymous class is a little troublesome. This section explains how to use it more simply.
The EventHandler interface contains only one method named handle. In Java 8, this kind of interface with only one method is called a functional interface. It can be treated like a substitute for a function object, or a function handled as a value.
What makes this possible is the lambda expression. Many people may not have used lambda expressions in Java 8, so let’s briefly summarize them.
A lambda expression is a feature that lets you write an instance implementation based on an anonymous class of a functional interface very simply. Using this setOnAction example, it can be changed as follows.
Ordinary Writing
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
// Write processing here.
}
});
Lambda Expression Writing
button.setOnAction((ActionEvent e) -> {
// Write processing here.
});
You can see that it becomes very simple. With a lambda expression, there is no need to write the method. In the case of a functional interface, there is only one method to call, so it is automatically determined when that method is called.
The example below rewrites the previous sample using a lambda expression.
package com.devkuma.javafx;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class App extends Application {
Label label;
TextField field;
Button button;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
label = new Label("This is JavaFX!");
field = new TextField();
button = new Button("Click");
// Specify action event processing.
button.setOnAction((ActionEvent e)-> {
String msg = "you typed: " + field.getText();
label.setText(msg);
});
BorderPane pane = new BorderPane();
pane.setTop(label);
pane.setCenter(field);
pane.setBottom(button);
Scene scene = new Scene(pane, 320, 120);
stage.setScene(scene);
stage.show();
}
}
Isn’t the event processing implementation now much easier to understand? Since you are using JavaFX with Java 8, make sure to remember how to write it with lambda expressions.