1. Introduction
JavaFX is a powerful tool designed to build application UI for different platforms. It provides not only UI components but different useful tools, such as properties and observable collections.
ListView component is handy to manage collections. Namely, we didn't need to define DataModel or update ListView elements explicitly. Once a change happens in the ObjervableList, it reflects in the ListView widget.
However, such an approach requires a way to display our custom items in JavaFX ListView. This tutorial describes a way to set up how the domain objects look in the ListView.
2. Cell Factory
2.1. Default Behavior
By default ListView in JavaFX uses the toString() method to display an object.
So the obvious approach is to override it:
public class Person {
String firstName;
String lastName;
@Override
public String toString() {
return firstName + " " + lastName;
}
}
This approach is ok for the learning and conceptual examples. However, it's not the best way.
First, our domain class takes on display implementation. Thus, this approach contradicts to single responsibility principle.
Second, other subsystems may use toString(). For instance, we use the toString() method to log our object's state. Logs may require more fields than an item of ListView. So, in this case, a single toString() implementation can't fulfill every module need.
2.2. Cell Factory to Display Custom Objects in ListView
Let's consider a better way to display our custom objects in JavaFX ListView.
Each item in ListView is displayed with an instance of ListCell class. ListCell has a property called text. A cell displays its text value.
So to customize the text in the ListCell instance, we should update its text property. Where can we do it? ListCell has a method named updateItem. When the cell for the item appears, it calls the updateItem. The updateItem method also runs when the cell changes. So we should inherit our own implementation from the default ListCell class. In this implementation, we need to override updateItem.
But how can we make ListView use our custom implementation instead of the default one?
ListView may have a cell factory. Cell factory is null by default. We should set it up to customize the way ListView displays objects.
Let's illustrate cell factory on an example:
public class PersonCellFactory implements Callback<ListView<Person>, ListCell<Person>> {
@Override
public ListCell<Person> call(ListView<Person> param) {
return new ListCell<>(){
@Override
public void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (empty || person == null) {
setText(null);
} else {
setText(person.getFirstName() + " " + person.getLastName());
}
}
};
}
}
CellFactory should implement a JavaFX callback. The Callback interface in JavaFX is similar to the standard Java Function interface. However, JavaFX uses a Callback interface due to historical reasons.
We should call default implementation of the updateItem method. This implementation triggers default actions, such as connecting the cell to the object and showing a row for an empty list.
The default implementation of the method updateItem calls setText, too. It then sets up the text that will be displayed in the cell.
2.3. Display Custom Items in JavaFX ListView With Custom Widgets
ListCell provides us with an opportunity to set up a custom widget as content. All we should do to display our domain objects in custom widgets is to use setGraphics() instead of setCell().
Supposing, we have to display each row as a CheckBox. Let's take a look at the appropriate cell factory:
public class CheckboxCellFactory implements Callback<ListView<Person>, ListCell<Person>> {
@Override
public ListCell<Person> call(ListView<Person> param) {
return new ListCell<>(){
@Override
public void updateItem(Person person, boolean empty) {
super.updateItem(person, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if (person != null) {
setText(null);
setGraphic(new CheckBox(person.getFirstName() + " " + person.getLastName()));
} else {
setText("null");
setGraphic(null);
}
}
};
}
}
In this example, we set the text property to null. If both text and graphic properties exist, the text will show beside the widget.
Of course, we can set up the CheckBox callback logic and other properties based on our custom element data. It requires some coding, the same way as setting up the widget text.
3. Conclusion
In this article, we considered a way to show custom items in JavaFX ListView. We saw that the ListView allows quite a flexible way to set it up. We can even display custom widgets in our ListView cells.
As always, the code for the examples is available over on GitHub.