TodoFX2
By John Kostaras
This article is an evolution of the TodoFX application. We shall see how to:
- add a graph
- port database access to JPA
Before we continue, download the TodoFX application from here.
Contents |
Introduction
Add a graph
JavaFX has very good support for graphs. We shall extend our TodoFX application by creating a statistics graph to display due date distribution.
We'll use a Bar Chart containing a bar for each month to show how many tasks expire in that particular month.
- Right-click on the
viewcontroller
package and selectNew --> Other --> JavaFX --> Empty FXML
as shown below. Click Next. - Provide
TaskStatistics
as the FXML Name. Click Next. - Check Use Java Controller and accept the default. Click Next.
- Click Finish.
- Double click on
TaskStatistics.fxml
to open it in SceneBuilder. - Select the root AnchorPane.
- Add a BarChart to the AnchorPane.
- Right-click on the BarChart and select Fit to Parent.
- Save the
.fxml
file
Open the TaskStatisticsController
and add the following code:
package todofx.viewcontroller;
import java.net.URL;
import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.XYChart;
import todofx.model.Task;
/**
* Task Statistics.
*
* @author ikost
*/
public class TaskStatisticsController implements Initializable {
@FXML
private BarChart<String, Integer> barChart;
@FXML
private CategoryAxis xAxis;
private final ObservableList<String> monthNames = FXCollections.observableArrayList();
/**
* Initializes the controller class.
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
// Get an array with the English month names.
String[] months = DateFormatSymbols.getInstance(Locale.ENGLISH).getMonths();
// Convert it to a list and add it to our ObservableList of months.
monthNames.addAll(Arrays.asList(months));
// Assign the month names as categories for the horizontal axis.
xAxis.setCategories(monthNames);
}
/**
* Sets the tasks to show the statistics for.
*
* @param tasks
*/
public void setTaskData(List<Task> tasks) {
// Count the number of tasks expiring in a specific month.
int[] monthCounter = new int[12];
tasks.stream()
.map(t -> t.getDueDate().getMonthValue() - 1)
.forEach(month -> monthCounter[month]++);
XYChart.Series<String, Integer> series = new XYChart.Series<>();
// Create a XYChart.Data object for each month. Add it to the series.
for (int i = 0; i < monthCounter.length; i++) {
series.getData().add(new XYChart.Data<>(monthNames.get(i), monthCounter[i]));
}
barChart.getData().add(series);
}
}
The BarChart<String, Integer>
uses the String
for the months and the Integer
for the number of tasks. In the initialize()
method, xAxis
is initialised with the list of months. setTaskData()
will be accessed by Main
to set the task data. It loops through all tasks and counts their due dates per month. Then it adds XYChart.Data
for every month to the data series. Each XYChart.Data
object will represent one bar in the chart.
In SceneBuilder, link the BarChart
to the barChart
(fx:id
) and select the CategoryAxis
and connect it to the xAxis
property.
To connect the TaskStatistics
to the rest of the application, add the following method in Main
:
/**
* Opens a dialog to show task statistics.
*/
public void showTaskStatistics() {
try {
// Load the fxml file and create a new stage for the popup.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("viewcontroller/TaskStatistics.fxml"));
AnchorPane page = (AnchorPane) loader.load();
Stage dialogStage = new Stage();
dialogStage.setTitle("Task Statistics");
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(primaryStage);
Scene scene = new Scene(page);
dialogStage.setScene(scene);
// Set the tasks into the controller.
TaskStatisticsController controller = loader.getController();
try {
controller.setTaskData(TaskManagerDB.getInstance().listAllTasks(true));
} catch (DatabaseException ex) {
Logger.getLogger(Main.class.getName())
.log(Level.SEVERE, null, ex);
}
dialogStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
Finally, open TaskMain
in SceneBuilder. Add a new menu Statistics
and a new menu item inside it Show Statistics...
.
Edit TaskMainController
by adding the following method:
/**
* Opens the task statistics.
*/
@FXML
private void handleShowTaskStatistics() {
mainApp.showTaskStatistics();
}
Don't forget to link the Show Statistics...
menu item to the handleShowTaskStatistics()
handler method (Inspector --> Code --> On Action).
If everything went OK, then you should be able to see a dialog box similar to the one below:
Support for JPA
JDBC is not used anymore in modern applications, when there is a choice to use an Object-Relational Mapping (ORM) library like the standard Java Persistence API (JPA).
References
- Lozano F. (2006), "A complete App using NetBeans 5", NetBeans Magazine, Issue 1, May, http://netbeans.org/download/magazine/01/nb01_completeapp.pdf
- Anderson G., Anderson P. (2014), JavaFX Rich Client Programming on the Netbeans Platform, Addison-Wesley.
- Dea C. et al. (2014), JavaFX 8 Introduction by Example, 2nd Ed., APress.
- Duodu E. (2015), "How to Create a JavaFX GUI using Scene Builder in NetBeans", IDR Solutions.
- Jacob M. (2014), "JavaFX 8 Tutorial", code.makery.
- Sharan K. (2014), Learn JavaFX 8, APress.
- Tamam M. (2015), JavaFX Essentials, APress.
- Vos J. et al. (2014), Pro JavaFX 8, APress.