In modern user interfaces, the need to display hierarchical data is common. Whether it’s organizing files in a file explorer, displaying organizational structures, or visualizing data relationships, presenting information in a tree-like structure is often the most effective way. JavaFX, a rich set of user interface controls for creating desktop applications, provides a powerful component called JavaFX TreeTableView that allows developers to build interactive hierarchical data displays with ease.
In this article, we will explore the JavaFX TreeTableView component and provide code examples to demonstrate its usage for creating hierarchical data displays.
What’s a TreeTableView?
TreeTableView is a JavaFX control that combines the functionality of a traditional table view with the hierarchical representation of a tree view. It allows you to display data in a tabular format where each row can have child rows, forming a tree-like structure. The top-level rows represent the main items, while the child rows represent the hierarchical data associated with those items.
Here are some key features of TreeTableView:
- Hierarchical Structure: The main feature of the TreeTableView is its ability to represent hierarchical data, making it ideal for data that has a parent-child relationship.
- Column-Based: Columns can be customized to display specific properties of the tree nodes. These columns can be sorted and resized just like in a TableView.
- Event Handling: TreeTableView supports various event handlers, allowing developers to respond to user interactions like node selection, expansion, and collapsing.
- Cell Factories: Developers can use cell factories to control the rendering and editing behavior of individual cells within the TreeTableView.
- Data Manipulation: Data can be dynamically added, removed, and modified within the TreeTableView, and these changes are automatically reflected in the UI.
Building a Simple TreeTableView
Let’s build a simple example to demonstrate how to use TreeTableView. In this example, we will create a hierarchical display of employees in an organization. Each employee has a name, position, and a list of subordinates.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Main extends Application {
private final BorderPane parent = new BorderPane();
@Override
public void init() throws Exception {
super.init();
this.buildUI();
}
private void buildUI() {
// Create the TreeTableView
TreeTableView<Employee> treeTableView = new TreeTableView<>();
TreeTableColumn<Employee, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
TreeTableColumn<Employee, String> positionColumn = new TreeTableColumn<>("Position");
positionColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("position"));
treeTableView.getColumns().addAll(nameColumn, positionColumn);
// Create root item
TreeItem<Employee> rootItem = new TreeItem<>(new Employee("Edward Nyirenda Jr.", "CEO"));
// Add child items
TreeItem<Employee> managerItem = new TreeItem<>(new Employee("Cherish Nyirenda", "Manager"));
managerItem.getChildren().addAll(
new TreeItem<>(new Employee("Andrew Phiri", "Employee")),
new TreeItem<>(new Employee("Benard Zulu", "Employee"))
);
rootItem.getChildren().add(managerItem);
treeTableView.setRoot(rootItem);
// Add the TreeTableView to the BorderPane
this.parent.setCenter(treeTableView);
}
@Override
public void start(Stage stage) throws Exception {
this.setupStage(stage);
}
private void setupStage(Stage stage) {
Scene scene = new Scene(this.parent, 640, 480);
// Set the stage title
stage.setTitle("JavaFX TreeTableView: Building Hierarchical Data Displays");
// Set the stage scene
stage.setScene(scene);
// Center the stage on the screen
stage.centerOnScreen();
// Show the stage on the screen
stage.show();
}
public static class Employee {
private String name;
private String position;
public Employee(String name, String position) {
this.name = name;
this.position = position;
}
public String getName() {
return name;
}
public String getPosition() {
return position;
}
}
}
In this example, we’ve defined a simple Employee class to hold the employee’s name and position. We’ve created a TreeTableView with two columns for name and position, and populated it with hierarchical data representing an organizational structure.
Handling Events
JavaFX TreeTableView also supports event handling, allowing you to respond to user interactions such as selection changes and expansion of nodes. Let’s add a selection listener to our example.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Main extends Application {
private final BorderPane parent = new BorderPane();
@Override
public void init() throws Exception {
super.init();
this.buildUI();
}
private void buildUI() {
// Create the TreeTableView
TreeTableView<Employee> treeTableView = new TreeTableView<>();
TreeTableColumn<Employee, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
TreeTableColumn<Employee, String> positionColumn = new TreeTableColumn<>("Position");
positionColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("position"));
treeTableView.getColumns().addAll(nameColumn, positionColumn);
// Create root item
TreeItem<Employee> rootItem = new TreeItem<>(new Employee("Edward Nyirenda Jr.", "CEO"));
// Add child items
TreeItem<Employee> managerItem = new TreeItem<>(new Employee("Cherish Nyirenda", "Manager"));
managerItem.getChildren().addAll(
new TreeItem<>(new Employee("Andrew Phiri", "Employee")),
new TreeItem<>(new Employee("Benard Zulu", "Employee"))
);
rootItem.getChildren().add(managerItem);
treeTableView.setRoot(rootItem);
// Add a selection listener
treeTableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
if (newValue != null) {
System.out.println("Selected item: " + newValue.getValue().getName());
}
});
// Add the TreeTableView to the BorderPane
this.parent.setCenter(treeTableView);
}
@Override
public void start(Stage stage) throws Exception {
this.setupStage(stage);
}
private void setupStage(Stage stage) {
Scene scene = new Scene(this.parent, 640, 480);
// Set the stage title
stage.setTitle("JavaFX TreeTableView: Building Hierarchical Data Displays");
// Set the stage scene
stage.setScene(scene);
// Center the stage on the screen
stage.centerOnScreen();
// Show the stage on the screen
stage.show();
}
public static class Employee {
private String name;
private String position;
public Employee(String name, String position) {
this.name = name;
this.position = position;
}
public String getName() {
return name;
}
public String getPosition() {
return position;
}
}
}
In this snippet, we’ve added a selection listener to the TreeTableView’s selection model. Whenever an item is selected, the listener prints out the name of the selected item.
Cell Rendering in TreeTableView
By default, a TreeTableView renders cells using their toString() method. However, this often results in a simple text representation that might not be sufficient for displaying complex or specialized data. Custom cell rendering empowers developers to define how cells should appear, giving them control over every aspect of the cell’s appearance.
Creating Custom Cell Factories
Custom cell rendering in TreeTableView is achieved by using cell factories. A cell factory is responsible for creating and configuring the cells within a column. It allows you to define how the data should be presented visually.
Let’s consider an example where we have a TreeTableView that displays employees, and we want to display a progress bar indicating their task completion:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class Main extends Application {
private final BorderPane parent = new BorderPane();
@Override
public void init() throws Exception {
super.init();
this.buildUI();
}
private void buildUI() {
// Create the TreeTableView
TreeTableView<Employee> treeTableView = new TreeTableView<>();
TreeTableColumn<Employee, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
TreeTableColumn<Employee, String> positionColumn = new TreeTableColumn<>("Position");
positionColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("position"));
TreeTableColumn<Employee, Double> progressColumn = new TreeTableColumn<>("Progress");
progressColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("progress"));
// Custom Cell Factory for the Progress Column
progressColumn.setCellFactory(column -> new TreeTableCell<>() {
final ProgressBar progressBar = new ProgressBar();
@Override
protected void updateItem(Double item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setGraphic(null);
} else {
progressBar.setProgress(item);
setGraphic(progressBar);
}
}
});
treeTableView.getColumns().addAll(nameColumn, positionColumn, progressColumn);
// Create root item
TreeItem<Employee> rootItem = new TreeItem<>(new Employee("Edward Nyirenda Jr.", "CEO", 0.8));
// Add child items
TreeItem<Employee> managerItem = new TreeItem<>(new Employee("Cherish Nyirenda", "Manager", 1.0));
managerItem.getChildren().addAll(
new TreeItem<>(new Employee("Andrew Phiri", "Employee", 0.9)),
new TreeItem<>(new Employee("Benard Zulu", "Employee", 0.5))
);
rootItem.getChildren().add(managerItem);
treeTableView.setRoot(rootItem);
// Add the TreeTableView to the BorderPane
this.parent.setCenter(treeTableView);
}
@Override
public void start(Stage stage) throws Exception {
this.setupStage(stage);
}
private void setupStage(Stage stage) {
Scene scene = new Scene(this.parent, 640, 480);
// Set the stage title
stage.setTitle("JavaFX TreeTableView: Building Hierarchical Data Displays");
// Set the stage scene
stage.setScene(scene);
// Center the stage on the screen
stage.centerOnScreen();
// Show the stage on the screen
stage.show();
}
public static class Employee {
private String name;
private String position;
private double progress;
public Employee(String name, String position, double progress) {
this.name = name;
this.position = position;
this.progress = progress;
}
public String getName() {
return name;
}
public String getPosition() {
return position;
}
public double getProgress() {
return progress;
}
}
}
In this example, we’ve created a custom cell factory for the progress column. The custom cell factory defines how each cell in the progress column should be rendered. We’ve used a ProgressBar control to visually represent the task completion progress.
Conclusion
The TreeTableView in JavaFX is a powerful tool for displaying hierarchical data in an organized and user-friendly manner. Its combination of tabular and tree-like views makes it an excellent choice for various applications, from file explorers to organizational charts. By using the provided code examples and customizing them to your needs, you can easily implement complex hierarchical displays in your JavaFX applications.
Sources:
I hope you found this code informative and useful. If you would like to receive more content, please consider subscribing to our newsletter!