Java FX Visual Components

Previously published on Today Software Magazine.

Tables represent one of the most powerful tools used in JavaFX for displaying data, supporting the following actions:

    • Reordering the columns displayed by the user
    • Multiple sorting of the columns
    • Resizing
    • Cell factories for customizing cell content

Several classes of JavaFX SDK API are used for data representation in a tabular form. Among these, the most important are: TableView, TableColumn and TableCell. We can populate a table from a data model and then we can apply a cell factory to it.

The facilities of TableView include:

  • The TableColumn API:
    • Cell factories support, which allows a customization of the cell content in both states: display and editing
    • Specification of mindWidth, prefWidth, maxWidth, as well as that of fixed size columns
    • Resizing by the user, on running
    • Column reordering by the user, on running
    • Support for column implantation
  • Different resizing policies which determine what happens when the user resizes the columns
  • Support for sorting on several columns by clicking on the column header (by pressing the Shift key while we are clicking on the header)

Table creation

Minimally, we need the following classes in order to create a table:

  • TableView <S>, where S is the type of the object containing the list of items from TableView
  • TableColumn <S,T>, where S is the type of the TableView generic type and T is the type of the content of all the cells of this TableColumn

TableView is built from a number of TableColumn instances. Each TableColumn is responsible for displaying and editing the content of a column. In addition, TableColumn contains properties for:

  • Resizing
  • Visibility
  • Header text display
  • The display of the implanted columns it may contain
  • The display of a context menu when the user clicks right on the column header area
  • Sorting possibilities

When we create a TableColumn instance, probably the most important properties to set are: the text (that is, what we display in the header of the column) and the cell value factory (used for populating individual cells of the column).

The TableCell<S, T> class represents the intersection of a line with a column in TableView. It contains the following properties:

  • tableColumn: the TableColumn instance behind TableCell
  • tableView, the TableView associated with TableCell
  • tableRow, the TableRow where the TableCell is placed

In order to identify the intersection, TableCell contains an index property.

Cell is used for an individual cell in a TableView. Each cell is associated to a single data item, represented by the item property. A cell is responsible with rendering the item residing in it, which is usually a text. A cell allows customization through a cell factory.

The Cell API is used for the virtualization of controls such as ListView, TreeView and TableView. A cell is a labelled control, used for rendering a unit in one of the above mentioned controls. Cell is responsible both for the display as well as for the editing of the item. Besides the text, Cell can be represented by other controls such as CheckBox, ChoiceBox or any Node such as HBox, GridPane or even Rectangle. Since ListView, TreeView, TableView, as well as other such controls can be used for displaying a large amount of data, it is not practical to create a Cell for each item of the control. Each cell is reused; this is what this virtualized control does.

Since Cell is a Control, it has a model behind. Its Skin is responsible for defining the look and layout, while the Behaviour is responsible for the manipulation of events and using this contained information to modify the state. Cell is edited through CSS, like any other control.

In order to specialize a cell used for a TableView we should provide an implementation of the callback cellFactory() function defined on TableView. Cell factory is called by the platform any time a new cell has to be created. The implementation of a cell factory is responsible for the creation of a Cell instance and for the configuration necessary for that Cell to react to the changes of its state.

Cell factory is responsible for the virtualization of container skins to render the predefined representation of a Cell item. For example, in a ListView it converts the items to a String and calls Labeled.setText (String). If we wish to specialize the cell used in ListView, we need to provide the implementation of the callback function defined on ListView.

Cell factory is called by the platform any time it determines that a cell should be created. For instance, a ListView has 10 million items. The creation of all those 10 million is very costly. Thus, the implementation of the ListView skin will create only as many cells as to fill up the visual space. If the visual space is resized, the system will decide whether it is necessary to create other cells. In this case, it will call cellFactory() (if there is one) in order to create a Cell implementation. If none is provided, then the predefined implementation is used.

ObservableList is the data model underlying TableView. A TableView instance is defined as:

TableView table = new TableView<>();

Thus, we have defined a primary table. The data model is created based on an ObservableList. We can set it directly in the TableView. For example:

ObservableList teamMembers = getTeamMembers();


Once the TableView item list is set, it is automatically updated any time the teamMembers list is modified. If the item list is available before TableView is instanced, it is possible for us to send it directly in the constructor.

What we also need to do is to divide the data contained in the model into one or several TableColumn instances. In order to create a two column TableView displaying firstName and lastName, we will use the following code:

public class DataTable extends Application {
    TableView table = new TableView<>();
    BorderPane root = new BorderPane();
    VBox mainBox = new VBox(); // Container for content
    final ObservableList teamMembers = FXCollections
            .observableArrayList(new Person("Ion", "Tech"), new Person("Petre","Petrescu"), new Person("Doru", "Dorescu"), new Person("Vasile", "Vasilescu"));

    public void init() {
        Label centerLbl = new Label("Persons");
        centerLbl.setStyle("-fx-font-size:16pt; -fx-font-weight:bold;");
        TableColumn<Person, String> firstNameCol = new TableColumn<Person, String>("First Name");
        firstNameCol    .setCellValueFactory(new PropertyValueFactory<Person, String>("firstName"));
        TableColumn<Person, String> lastNameCol = new TableColumn<Person, String>("Last Name");
        lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("lastName"));
        table.getColumns().setAll(firstNameCol, lastNameCol);

    public void start(Stage primaryStage) {
        primaryStage.setTitle("Data Table");
        primaryStage.setScene(new Scene(root, 400, 300));;

    public static void main(String[] args) {

By this, we have completely defined the minimal properties required in order to be able to create a TableView instance. No other properties of the Person class will be displayed, since there are no other defined TableColumns. The implementation of a cell factory is responsible not only with the creation of the Cell instance, but also with the configuration of that cell. In the following example, we have created a Callback class whose attributes are instances of the TextAlignment and Format classes, with the parameters:

  • S, the generic type of TableView
  • T, the content type in all the TableColumn cells.
public class FormattedTableCellFactory<S, T> implements
    Callback<TableColumn<S, T>, TableCell<S, T>> {
    private TextAlignment alignment;
    private Format format;
    public TextAlignment getAlignment() {
        return alignment;

    public void setAlignment(TextAlignment alignment) {
        this.alignment = alignment;

    public Format getFormat() {
        return format;

    public void setFormat(Format format) {
        this.format = format;

    public TableCell<S, T> call(TableColumn<S, T> p) {
        TableCell<S, T> cell = new TableCell() {
            public void updateItem(Object item, boolean empty) {
                if (item == getItem())

                super.updateItem(item, empty);
                if (item == null) {
                } else if (format != null) {
                } else if (item instanceof Node) {

                    super.setGraphic((Node) item);
                } else {

        switch (alignment) {
        case CENTER:

        case RIGHT:


        return cell;


FormattedTableCellFactory<Person, String> xx = new FormattedTableCellFactory<>();


The benefices of using CSSs in order to set the style of a table consist in time efficiency and memory efficiency, the easiness of using and building libraries for the cells, the easiness of customization of display formatting.

We are using CSSs to set the colours of the cell:

  • Each cell can be edited right from the CSS. For instance, if we wish to change the predefined colour of the background of each cell in the TableView to white, we have to use the following CSS:
.table-cell {
 -fx-padding: 3 3 3 3;
 -fx-background-color: white; 
  • In order to set the colours of some cells selected in a TableView to blue, we will use the following CSS:
.table-cell:selected { 
-fx-background-color: blue; 

In order for table-cell:selected to work, we have to set the cellSelectionEnabled to true.

Many cell implementations extend the IndexedCell instead of the Cell. This allows the adding of other two pseudo-classes: odd and even. With these we can get alternating colouring of the lines by means of a CSS such as:


Thank you for reading our article and we are looking forward, as usual, to discuss it further with you.