Tuesday 22 July 2014

JavaFX: Pie chart using dynamic data

The JavaFX SDK contains some pretty nice charts which you can use without much configuration. The documentation from JavaFX provides tutorials for each of the 6 chart types plus a section about styling the charts using CSS.

In the following examples we use a PieChart and feed it with dynamically changing data. We will count each distinct character of an input text field and display the new values while the user changes the input value.

package sample;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class PieChartExample extends Application {

    // list that holds the values you want to display on the chart
    private static ObservableList<PieChart.Data> list = FXCollections.observableList(new ArrayList<PieChart.Data>());

    @Override
    public void start(Stage primaryStage) throws Exception {

        VBox root = new VBox();
        Scene scene = new Scene(root, 1024, 768);

        // text field for user input
        final TextField textField1 = new TextField();
        textField1.setPrefWidth(1000);

        // listener will be invoked whenever the user changes the value
        textField1.textProperty().addListener(new ChangeListener<String>() {

            @Override
            public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue) {

                list.clear();
                
                // fill the list containing data for the chart with the values from the map
                for(Map.Entry<String, Integer> entry : countCharactersOfString(newValue).entrySet()) {
                    list.add(new PieChart.Data(entry.getKey(), entry.getValue()));
                }
            }
        });

        // set up the pie chart
        PieChart pieChart = new PieChart();
        pieChart.setData(list);
        pieChart.setLegendVisible(false);
        pieChart.setPrefHeight(900);

        root.getChildren().addAll(textField1, pieChart);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    /**
     * Count all distinct characters of a string an put them in a map.
     * 
     * @param input String from user input
     * @return map with character as key and occurrence as value
     */
    private Map<String, Integer> countCharactersOfString(String input) {

        Map<String, Integer> countMap = new HashMap<String, Integer>();
        char[] charArray = input.toCharArray();

        for (char currentChar : charArray) {
            String current = String.valueOf(currentChar);
            if (countMap.containsKey(current)) {
                countMap.replace(current, countMap.get(current) + 1);
            } else {
                countMap.put(current, 1);
            }
        }

        return countMap;
    }
}

If you enter some characters in the textfield at the top you will see that the pie chart dynamically changes its values. We listen to changes in the input text field and count the entered characters and the ObservableList reflects every manipulation we do in the list also to the chart.