Creating a TreeTableView

Sometimes, it is useful to combine a tree and a table. This is especially important when the data that has to be visualized is hierarchical and each entry contains the same type of properties. In this case, the TreeTableView class provides a great solution. As you can see in Figures 6-7 and 6-8, a TreeTableView provides the functionality offered by a TreeView, but for every entry that is shown, we can also show additional columns in a TreeTableView.

The data in a TreeTableView is created using TreeItem instances. In our StarterApp program, we created data in the getFamily() method of the model, as shown in Listing 6-10.

Listing 6-10. Create Hierarchical Data

public TreeItem<Person> getFamilyTree() {
    Random random = new Random();
    TreeItem<Person> root = new TreeItem();
    for (int i = 0; i < 5; i++) {
        Person parent = new Person("Parent " + i, "LastName" + i, "Phone" + i);
        TreeItem<Person> parentItem = new TreeItem(parent);
        for (int j = 0; j < random.nextInt(4); j++) {
            Person child = new Person("Child " + i + "-" + j, "LastName" + i, "Phone" + j);
            TreeItem<Person> childItem = new TreeItem(child);
            parentItem.getChildren().add(childItem);
            for (int k = 0; k < random.nextInt(4); k++) {
                Person grandChild = new Person("Grandchild " + i + "-" + j + "-" + k, "LastName" + i, "Phone" + k);
                TreeItem<Person> grandChildItem = new TreeItem(grandChild);
                childItem.getChildren().add(grandChildItem);
            }
        }
        root.getChildren().add(parentItem);
    }
    return root;
}

In this code, we create a number of persons, starting with five “parents.” We then add a random number of children (between zero and three) to each parent, and each of the children can have a random number of children itself—so-called grandchildren. All parents are added to a root node on the TreeItem. Rendering this hierarchical data using a TreeTableView is demonstrated in Listing 6-11.

Listing 6-11. Rendering Hierarchical Data Using a TreeTableView

Node createTreeTableDemoNode() {
        TreeTableView<Person> treeTableView = new TreeTableView(model.getFamilyTree());
        TreeTableColumn<Person, String> firstNameColumn = new TreeTableColumn("First Name");
        firstNameColumn.setCellValueFactory(new TreeItemPropertyValueFactory("firstName"));
        firstNameColumn.setPrefWidth(180);
        TreeTableColumn lastNameColumn = new TreeTableColumn("Last Name");
        lastNameColumn.setCellValueFactory(new TreeItemPropertyValueFactory("lastName"));
        lastNameColumn.setPrefWidth(180);
        TreeTableColumn phoneColumn = new TreeTableColumn("Phone Number");
        phoneColumn.setCellValueFactory(new TreeItemPropertyValueFactory("phone"));
        phoneColumn.setPrefWidth(180);
        treeTableView.getColumns().addAll(firstNameColumn, lastNameColumn, phoneColumn);
        treeTableView.getSelectionModel().selectedItemProperty().addListener((ObservableValue<?
        extends TreeItem<Person>>observable, TreeItem<Person> oldValue, TreeItem<Person> newValue) -> {
            Person selectedPerson = newValue.getValue();
            System.out.println(selectedPerson + " chosen in TreeTableView");
        });
        treeTableView.setShowRoot(false);
        return treeTableView;
}

As you can see from this code, the TreeTableView looks rather similar to the TableView. There are some obvious differences, though. Rather than using TableColumn instances to describe the columns, a TreeTableView requires one to use TreeTableColumn instances. Also, if we want to set the CellValueFactory for a specific column, we can use the convenience class TreeItemPropertyValueFactory, which will make sure the value of the property with the supplied name is rendered in the particular call for a specific column. We now move on to the next tab, ScrollPane/Miscellaneous.