How to add a tooltip to a TableView header cell in JavaFX 8

    TableColumn<Person, String> firstNameCol = new TableColumn<>();
    Label firstNameLabel = new Label("First Name");
    firstNameLabel.setTooltip(new Tooltip("This column shows the first name"));
    firstNameCol.setGraphic(firstNameLabel);

The solution

Alternatively, you can look up the label that's already there and just give it a tool tip.

In order to .lookup() an element the table needs to be rendered already. To be sure that your code runs after the table is rendered, just wrap your code in a Platform.runlater().

// We need to do this after the table has been rendered (we can't look up elements until then)
Platform.runLater(() -> {
    // Prepare a tooltip
    Tooltip tooltip = new Tooltip("This is a super cool control; here's how to work it...");
    tooltip.setWrapText(true);
    tooltip.setMaxWidth(200);

    // Get column's column header
    TableColumnHeader header = (TableColumnHeader) historyTable.lookup("#" + column.getId());

    // Get column header's (untooltipped) label
    Label label = (Label) header.lookup(".label");

    // Give the label a tooltip
    label.setTooltip(tooltip);

    // Makes the tooltip display, no matter where the mouse is inside the column header.
    label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
}

The solution at scale

If you want to do this for your whole set of columns, you might put them all in a LinkedHashMap (this will just help you organizationally by keeping your columns associated with their messages):

// We'll use this to associate columns with messages for right now.
LinkedHashMap<TableColumn<Person, String>, String> tableColumns = new LinkedHashMap<>();

// Each column gets a helpful message
tableColumns.put(numberOfBoxesColumn, "The total number of boxes that have arrived");
tableColumns.put(backordersColumn, "Use these columns as a measure of how urgently the Purchase Order needs to be processed.");
/*... put each column in along with it's message */

... then use a for-each loop to loop through and give each column a tooltip ...

// Make a tooltip out of each message. Give each column('s label) it's tooltip.
for (Map.Entry<TableColumn<Person, String>, String> pair : tableColumns.entrySet()) {
    
    TableColumn<Person, String> column;
    String message;

    // Get the column and message
    column = pair.getKey();
    message = pair.getValue();

    // Prepare a tooltip
    Tooltip tooltip = new Tooltip(message);
    tooltip.setWrapText(true);
    tooltip.setMaxWidth(200);

    // Get column's column header
    TableColumnHeader header = (TableColumnHeader) historyTable.lookup("#" + column.getId());

    // Get column header's (untooltipped) label
    Label label = (Label) header.lookup(".label");

    // Give the label a tooltip
    label.setTooltip(tooltip);

    // Makes the tooltip display, no matter where the mouse is inside the column header.
    label.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
}

NOTE: I tried the combination of Jesper and James' solutions before coming up with this one. Sadly, when I did, the CSS caused all of my labels to disappear when I look at my .fxml layout in SceneBuilder. And we just can't have that, can we? (okay maybe I just couldn't have that).

Where're mah labels!


This is an extended answer to James_D. (I don't have the reputation to comment):

To make the label connect with textProperty of the column and just hide the original text, so it does not mess up the rest of the table functionality:

nameLabel.textProperty().bindBidirectional(textProperty());
nameLabel.getStyleClass().add("column-header-label");
nameLabel.setMaxWidth(Double.MAX_VALUE); //Makes it take up the full width of the table column header and tooltip is shown more easily.

css:

.table-view .column-header .label{
    -fx-content-display: graphic-only;
}
.table-view .column-header .label .column-header-label{
    -fx-content-display: text-only;
}