JavaFX for Swing Developers


3 Enriching Swing Applications with JavaFX Functionality

In this chapter you learn how to intermix a Swing table and JavaFX bar chart in a single application.

This chapter starts with a Swing application and provides an example of how to enrich the Swing application by adding JavaFX functionality.

Sample Swing Application

Many real-world projects employ Swing applications that deal with tables. You can continue using the existing code and still take an advantage of JavaFX APIs. For example, you can add a JavaFX bar chart to provide a colorful illustration of the tabular data. This chapter provides the SwingInterop example that handles a Swing table and a JavaFX bar chart. As you change the data in a table cell, the bar chart immediately updates.

Start with the sample application that has only the Swing table shown in Figure 3-1.

Figure 3-1 Swing JTable Application Window

Description of Figure 3-1 follows
Description of "Figure 3-1 Swing JTable Application Window"

This application consists of two classes:

The SampleTableModel class inherits from the AbstractTableModel class and defines the table.

The SwingInterop class inherits from the JApplet class and is the basic class of the application. Its main method calls the run method on the Event Dispatch Thread (EDT) to create the graphical user interface (GUI). The run method creates a JFrame object and a JApplet object, and initializes the JApplet object with an instance of the SwingInterop class. Then it calls the init method, which creates the table and adds the table to the content pane of the applet.

You can see the implementation of both classes by using the links in the sidebar.

Integrating JavaFX Bar Chart

To provide data for a bar chart, modify the SampleTableModel class by adding a new class variable (bcData) and a method that retrieves data from the table and returns the data in the format appropriate for the bar chart. The implementation of the getBarChartData method is shown in Example 3-1.

Example 3-1

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.chart.BarChart;

public class SampleTableModel extends AbstractTableModel {
    private static ObservableList<BarChart.Series> bcData;

    public ObservableList<BarChart.Series> getBarChartData() {
        if (bcData == null) {
            bcData = FXCollections.<BarChart.Series>observableArrayList();
            for (int row = 0; row < getRowCount(); row++) {
                ObservableList<BarChart.Data> series =
                for (int column = 0; column < getColumnCount(); column++) {
                    series.add(new BarChart.Data(getColumnName(column),
getValueAt(row, column)));
                bcData.add(new BarChart.Series(series));
        return bcData;
//rest of the SampleTableModel class code

The SwingInterop class overrides the JApplet.init method to create the content pane of the application. Modify the init method to create a JFXPanel object to hold the JavaFX bar chart and a JSplitPane object to hold both the JavaFX chart and the table. The required changes to the init method are shown in bold in Example 3-2.

Example 3-2

public void init() {
    tableModel = new SampleTableModel();
    // create javafx panel for charts
    chartFxPanel = new JFXPanel();
    chartFxPanel.setPreferredSize(new Dimension(PANEL_WIDTH_INT, PANEL_HEIGHT_INT));

    //create JTable
    JTable table = new JTable(tableModel);
    SwingInterop.DecimalFormatRenderer renderer = 
new SwingInterop.DecimalFormatRenderer();
    for (int i = 0; i < table.getColumnCount(); i++) {
    JScrollPane tablePanel = new JScrollPane(table);
    tablePanel.setPreferredSize(new Dimension(PANEL_WIDTH_INT, 
    JPanel chartTablePanel = new JPanel();
    chartTablePanel.setLayout(new BorderLayout());

    //Create split pane that holds both the bar chart and table
    JSplitPane jsplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
    chartTablePanel.add(chartFxPanel, BorderLayout.CENTER);

    //Add the split pane to the content pane of the application
    add(jsplitPane, BorderLayout.CENTER);

To get rid of a syntax error, add import statements and the definition of the chartFxPanel class variable to the SwingInterop class as shown in Example 3-3.

Example 3-3

import javafx.embed.swing.JFXPanel;
import javax.swing.*;

public class SwingInterop extends JApplet {
    private static JFXPanel chartFxPanel;
// rest of the SwingInterop class code here

You prepared the UI of your Swing application to render JavaFX data. The next step is creating the JavaFX scene. Because the JavaFX scene must be created on the JavaFX Application thread, wrap your code into a Runnable object as shown in Example 3-4. Add this code at the end of the init method.

Example 3-4

Platform.runLater(new Runnable() {
    public void run() {

Add the import statement shown in Example 3-5 to the SwingInterop class.

Example 3-5

import javafx.application.Platform;

Implement the createScene method of the SwingInterop class as shown in Example 3-6. Add the import statements and define the instance variable chart.

Example 3-6

import javafx.scene.Scene;
import javafx.scene.chart.Chart;

private void createScene() {
    chart = createBarChart();
    chartFxPanel.setScene(new Scene(chart));

The createBarChart method creates the chart diagram and adds a change listener to the table. Note that any change of JavaFX data must happen on the JavaFX thread. For this reason, wrap the code in the event handler, which updates the JavaFX chart, into a Runnable object and pass it to the Platform.runLater method. The implementation of the createBarChart method is shown in Example 3-7.

Example 3-7

private BarChart createBarChart() {
    CategoryAxis xAxis = new CategoryAxis();
    double tickUnit = tableModel.getTickUnit();
    NumberAxis yAxis = new NumberAxis();
    yAxis.setLabel("Units Sold");

    final BarChart chart = new BarChart(xAxis, yAxis, tableModel.getBarChartData());
    tableModel.addTableModelListener(new TableModelListener() {
        public void tableChanged(TableModelEvent e) {
            if (e.getType() == TableModelEvent.UPDATE) {
                final int row = e.getFirstRow();
                final int column = e.getColumn();
                final Object value = 
((SampleTableModel) e.getSource()).getValueAt(row, column);
                Platform.runLater(new Runnable() {
                    public void run() {
                        XYChart.Series<String, Number> s = 
(XYChart.Series<String, Number>) chart.getData().get(row);
                        BarChart.Data data = s.getData().get(column);
    return chart;

Add the import statements shown in Example 3-8.

Example 3-8

import javafx.collections.FXCollections;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;

Rename the title of the frame to "Swing JTable and Bar Chart" and run the SwingInterop application.

The application window is shown in Figure 3-2.

Figure 3-2 SwingInterop Application Window

Description of Figure 3-2 follows
Description of "Figure 3-2 SwingInterop Application Window"