TweetWallFX – Part 4

JavaFX comes along with a highly customizable and powerful chart API out of the box: bar chart, pie chart, area chart and much more. And this API also provides you event handler to provide a high user experience allowing end-users to interact with the charts. Maybe you already know JFreeChart which still is a reference in the Java SE world but also used in some other third party libraries for Java EE. You can see the JavaFX Chart API as a really nice evolution of JFreeChart: powerful, highly customizable and out of the box!

TweetWallFX uses this API in order to display some statistics about the number of tweets captured by the wall. Typically it displays the number of tweets by hour with two main functions:

display the number of tweets while having the mouse over a symbol on the chart;

display the number of tweets by minutes when clicking on a chart symbol.

In this article we’ll focus on displaying the tweets by hour, customize our chart and define a little “mouse over” interaction.

Shall we chart?

It begins to be cleared that we’ll use a FXML file for the view and a controller. So let’s start with the FXML, named statistics-controller.fxml. As you will see, the root element is a Tab because we’ll display our chart in a specific tab, coming along with a wall.

I’ll focus on the chart creation, which is here a line chart with only one serie. As you can see I declare my <xAxis>…</xAxis> which represents the hours where tweets have been generated. It is a CategoryAxis because here, the value will be a string like 20h. The values for this axis are stored in a list, created here using <FXCollections … />. The Y axis is an axis which values are a number, so a NumberAxis. You can notice the <tickLabelFormatter /> that is a class that formats the value on the Y axis. Typically the one used won’t display decimals and looks like this:

Like the axises we create a serie for our values, which are also stored inside a list created using the FXCollections factory. Before creating the controller, we will create a CSS file, statistics-style.css, in order to customize the look of our chart:

In this file we redefine the color of the text for the labels, the chart title, the color of the lines and, the most interesting part, the line symbol. You can see the -fx-shape attribute that has a “strange” value. This value is simply the SVG path drawing a twitter bird! Not kidding! JavaFX supports natively SVG! You can use it like here in CSS, or in your FXML using the SVGPath object. And to conclude with the UI part, we’ll create a FXML file, tooltip.fxml, to display a tooltip when having the mouse over a line symbol:

You can see how to apply a CSS file to a Node, here our chart. We’re also adding a listener to our list of statistics in order to handle changes into hit. This listener is the following inner-class:

private class StatisticsChangeListener implements ListChangeListener<TweetStatistics> {
@Override
public void onChanged(Change<? extends TweetStatistics> event) {
// We retrieve the one and only serie defined in the chart.
// The serie's been created in the FXML file
XYChart.Series<String, Integer> series = StatisticsController.this.chart.getData().get(0);
boolean dataExists;
short index;
// Many events could have occurred, this is the reason of this loop
while(event.next()) {
// We get the sublist that contains modifications
// We get the list this way in order not to care
// if it was additions or removals
List<TweetStatistics> modifications = (List<TweetStatistics>) event.getList().subList(event.getFrom(), event.getTo());
// For each modifications, we look if the data already exists and need an update.
// If the data doesn't exist, add it
for(TweetStatistics ts : modifications) {
index = 0;
dataExists = false;
while(!dataExists && index < series.getData().size()) {
dataExists = series.getData().get(index).getXValue().equals(ts.getHour() + "h");
index++;
}
// If the data does not exist, create it
if(!dataExists) {
// The third argument of the constructor for a Data is the
// extra value
Data<String, Integer> data = new Data<String, Integer>(
String.format("%1$sh", ts.getHour()),
ts.getTotalTweets(), ts);
series.getData().add(data);
data.getNode().addEventHandler(EventType.ROOT, new ChartDataEventHandler(data));
} else {
Data<String, Integer> data = series.getData().get(index-1);
data.setYValue(ts.getTotalTweets());
}
}
}
}
}

So far so good. Now another inner-class, the ChartDataEventHandler class that will handle the mouse over a line symbol of the chart, as well as the click. We’ll just cover the mouse over event.