Documentation



JavaFX: Interoperability

6 Implementing a Swing Application in JavaFX

In this chapter, you consider a Swing application and learn how to implement it in JavaFX.

For the purpose of this chapter, get familiar with the Converter application shown in Figure 6-1. This application converts distance measurements between metric and U.S. units.

Figure 6-1 Converter Application in Java

Description of Figure 6-1 follows
Description of "Figure 6-1 Converter Application in Java"

Analyzing the Converter Application Developed in Swing

For more information about the implementation of this example in the Java programming language, see How to Use Panels and Using Models trails in the Swing tutorial. In particular, the graphical user interface (GUI) is discussed in the trail about the panels.

To learn the code of the Converter application, download its NetBeans project or the source files available at the example index.

Swing components use models. If you look at the contents of the project, you notice the ConverterRangeModel and FollowerRangeModel classes that define models for the Converter application.

The Converter application consists of the following files:

  • ConversionPanel.java — contains a custom JPanel subclass to hold components

  • Converter.java — contains the main application class

  • ConverterRangeModel.java — defines the top slider's model

  • FollowerRangeModel.java — defines the bottom slider's model

  • Units.java — creates Unit objects

Note that the synchronization between each text field and its slider is implemented by event handlers that listen for changes in values.

Planning the Converter Application in JavaFX

The Converter application contains two similar panels that hold components such as a text field, slider, and combo box. The panels have titles. The TitlePane class from the javafx.scene.control package ideally suits the GUI of the Converter application.

In what follows, you will implement the ConversionPanel class and add two instances of this class to the graphical scene of the Converter application.

First, note that the components within a single ConversionPanel object should be synchronized as follows. Whenever you move the knob on the slider, you must update the value in the text field and vice versa: Whenever you change the value in the text field, you must adjust the position of the knob on the slider.

As soon as you choose another value from the combo box, you must update the value of the text field and, hence, the position of the knob on the slider.

Second, note that both ConversionPanel objects should be synchronized. As soon as changes happen on one panel, the corresponding components on another panel must be updated.

It is suggested that you implement synchronization between the panels using the DoubleProperty object, called meters, and listen to changes in the properties of the text fields and combo boxes by creating and registering two InvalidationListener objects: fromMeters and toMeters. Whenever the property of the text field on one panel changes, the invalidated method of the attached InvalidationListener object is called, which updates the meters property. Because the meters property changes, the invalidated method of the InvalidationListener object, attached to the meters property, is called, which updates the corresponding text field on another panel.

Similarly, whenever the property of the combo box on one panel changes, the invalidated method of the attached InvalidationListener object is called, which updates the text field on this panel.

To provide synchronization between the value of the slider and the value of the meters object, use bidirectional binding.

For more information about JavaFX properties and binding, see Using JavaFX Properties and Binding.

Creating the Converter Application in JavaFX

Create a new JavaFX project in NetBeans IDE and name it Converter. Copy the Unit.java file from the Swing application to the Converter project. Add a new java class to this project and name it ConversionPanel.java.

Standard JavaFX Pattern to Create the GUI

Before you start creating the GUI of the Converter application in JavaFX, see the standard pattern of GUI creation in Swing applications, as shown in Example 6-1.

Example 6-1

public class Converter { 
    private void initAndShowGUI() {
        ...
    }
    public static void main(String[] args) { 
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                initAndShowGUI();
            }
        });
    }
}

To map this pattern to JavaFX, you extend the javafx.application.Application class, override the start method, and call the main method, as shown in Example 6-2.

Example 6-2

import javafx.application.Application;
import javafx.stage.Stage;

public class Converter extends Application {
    @Override
    public void start(Stage t) {
        ...
    }
    public static void main(String[] args) {
        launch(args);
    }
}

When you create a new JavaFX project in the NetBeans IDE, this pattern is automatically generated for you. However, it is important that you understand the basic approach to GUI creation in JavaFX, especially if you use a text editor.

Containers and Layouts

In Swing, containers and layout managers are different entities. You create a container, such as a JPanel or JComponent object, and set a layout manager for this container. You can assign a specific layout manager and write.add() in your code or assign none of the layout managers.

In JavaFX, the container itself takes care of laying out its child nodes. You create a specific layout pane, such as a Vbox, FlowPane, or TitledPane object, and then add content to the list of its child nodes using the.getChildren().add()methods.

There are several layout container classes in JavaFX, called panes, some of which have their counterparts in Swing, such as the FlowPane class in JavaFX and FlowLayout class in Swing.

For more information, see Working With Layouts in JavaFX.

UI Controls

JavaFX SDK provides a set of standard UI controls. Some of the UI controls have their counterparts in Swing such as the Button class in JavaFX and JButton in Swing; Slider in JavaFX and JSlider in Swing; and TextField in JavaFX and JTextField in Swing.

To implement the Converter application in JavaFX, you can use the standard UI controls provided by the TextField, Slider, and ComboBox classes.

For more information, see Using JavaFX UI Controls.

Mechanism of Getting Notifications on User Actions and Binding

In Swing, you can register a listener on any component and listen for changes in the component properties, such as size, position, or visibility; or listen for events, such as whether the component gained or lost the keyboard focus; or whether the mouse was clicked, pressed, or released over the component.

In JavaFX, each object has a set of properties for which you can register a listener. The listener is called whenever a value of the property changes.

Note that an object can be registered as a listener for changes in another object's properties. Thus, you can use the binding mechanism to synchronize some properties of two objects.

Creating the ConversionPanel Class

The ConversionPanel class is used to hold components: a text field, a slider, and a combo box. When creating the graphical scene of the Converter application, you add two instances of the ConversionPanel class to the graphical scene. Add the import statement for the TitledPane class and extend the ConversionPanel class as shown in Example 6-3.

Example 6-3

import javafx.scene.control.TitledPane;

public class ConversionPanel extends TitledPane {

}

Creating Instance Variables for UI Controls

Add import statements for the TextField, Slider, ComboBox controls and define instance variables for the components as shown in Example 6-4.

Example 6-4

import java.text.NumberFormat;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;

private ComboBox<Unit> comboBox;
private Slider slider;
private TextField textField;

Creating DoubleProperty and NumberFormat Objects

Add the import statement for the DoubleProperty and NumberFormat classes and create a DoubleProperty object named meters as shown in Example 6-5. The meters object is used to ensure the synchronization between two ConversionPanel objects.

Example 6-5

import javafx.beans.property.DoubleProperty;

private DoubleProperty meters;
provate numberFormat;

Laying Out the Components

To lay out the text field and the slider, use the VBox class. To lay out both of these components and a combo box, use the HBox class. Add the import statements for the ObservableList class and implement the constructor of the ConversionPanel class as shown in Example 6-6.

Example 6-6

import javafx.collections.ObservableList;


public ConversionPanel(String title, ObservableList<Unit> units, 
DoubleProperty meters) {
    setText(title);
    setCollapsible(false);

    numberFormat = NumberFormat.getNumberInstance();
    numberFormat.setMaximumFractionDigits(2);

    textField = new TextField();
    slider = new Slider(0, MAX, 0);
    comboBox = new ComboBox(units);
    comboBox.setConverter(new StringConverter<Unit>() {
        
        @Override
        public String toString(Unit t) {
            return t.description;
        }
        
        @Override
        public Unit fromString(String string) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    })
    VBox vbox = new VBox(textField, slider);
    HBox hbox = new HBox(vbox, comboBox);
    setContent(hbox);
    this.meters = meters;
    
    comboBox.getSelectionModel().select(0);
}

The last line of code selects a value in the ComboBox object.

Creating InvalidationListener Objects

To listen to changes in the properties of the text fields and combo boxes, create the InvalidationListener objects fromMeters and toMeters as shown in Example 6-7.

Example 6-7

import javafx.beans.InvalidationListener;

private InvalidationListener fromMeters = t -> {
    if (!textField.isFocused()) {
        textField.setText(numberFormat.format(meters.get() / getMultiplier()));
    }
};

private InvalidationListener toMeters = t -> {
    if (!textField.isFocused()) {
        return;
    try {
        meters.set(numberFormat.parse(textField.getText()).doubleValue() *
getMultiplier());
    } catch (ParseException | Error | RuntimeException ignored) {
    }
};

Adding Change Listeners to Controls and Ensuring Synchronization

To provide the synchronization between the text fields and combo boxes, add change listeners as shown in Example 6-8.

Example 6-8

meters.addListener(fromMeters);
comboBox.valueProperty().addListener(fromMeters);
textField.textProperty().addListener(toMeters);
fromMeters.invalidated(null);

Create a bidirectional binding between the value of the slider and the value of the meters object as shown in Example 6-9.

Example 6-9

slider.valueProperty().bindBidirectional(meters);

When a new value is typed in the text field, the invalidated method of the toMeters listener is called, which updates the value of the meters object.

Creating the Converter Class

Open the Converter.java file that was automatically generated by the NetBeans IDE and remove all of the code except for the main method. Then, press Ctrl (or Cmd)+Shift+I to correct the import statements.

Defining Instance Variables

Add import statements for the ObservableList, DoubleProperty, and SimpleDoubleProperty classes and create metricDistances, usaDistances, and meters variables of the appropriate types as shown in Example 6-10.

Example 6-10

import javafx.beans.property.DoubleProperty;
import javafx.collections.ObservableList;
import javafx.beans.property.SimpleDoubleProperty;

private ObservableList<Unit> metricDistances;
private ObservableList<Unit> usaDistances;
private DoubleProperty meters = new SimpleDoubleProperty(1);

Creating the Constructor for the Converter Class

In the constructor for the Converter class, create Unit objects for the metric and the U.S. distances as shown in Example 6-11. Add the import statement for the FXCollections class. Later, you will instantiate two ConversionPanel objects with these units.

Example 6-11

import javafx.collections.FXCollections;

public Converter() {
metricDistances = FXCollections.observableArrayList(
        new Unit("Centimeters", 0.01),
        new Unit("Meters", 1.0),
        new Unit("Kilometers", 1000.0));

usaDistances = FXCollections.observableArrayList(
        new Unit("Inches", 0.0254),
        new Unit("Feet", 0.305),
        new Unit("Yards", 0.914),
        new Unit("Miles", 1613.0));
}

Creating the Graphical Scene

Override the start method to create the graphical scene for your Converter application. Add two ConversionPanel objects to the graphical scene and lay out them vertically. Note that two ConversionPanel objects are instantiated with the same meters object. Use the VBox class as a root container for the graphical scene. Instantiate two ConversionPanel objects as shown in Example 6-12.

Example 6-12

@Override
public void start(Stage stage) {
    VBox vbox = new VBox(
            new ConversionPanel(
                    "Metric System", metricDistances, meters),
            new ConversionPanel(
                    "U.S. System", usaDistances, meters));
    Scene scene = new Scene(vbox);
    
    stage.setTitle("Converter");
    stage.setScene(scene);
    stage.show();
}

You can view the source code and download the NetBeans project of the Converter application in JavaFX using the links at the bottom of this document.

The Converter application in JavaFX is shown in Figure 6-2.

Figure 6-2 Converter Application in JavaFX

Description of Figure 6-2 follows
Description of "Figure 6-2 Converter Application in JavaFX"

Compare the two applications with the same functionality implemented using the Swing library and JavaFX.

Not only does the application in JavaFX contain three files as compared with five files of the Swing application, but the code in JavaFX is cleaner. The applications also differ in look and feel.

Application Files

Source Code 

NetBeans Projects 

Close Window

Table of Contents

JavaFX: Interoperability

Expand | Collapse