In Java FX 2.x TableView is one of the controls you can't set a tooltip directly. You must set tooltips for TableCell or TableRow instead. The technique used in this turorial is very similar for both controls. Let's take a look!

Tooltips on TableCell

myTableColumn.setCellFactory(new Callback<TableColumn<DataModel, Object>, TableCell<DataModel, Object>>() {
	@Override
	public TableCell<DataModel, Object> call(TableColumn<DataModel, Object> p) {
		return new TableCell<DataModel, Object>() {
			@Override
			public void updateItem(Object t, boolean empty) {
				super.updateItem(t, empty);
				if (t == null) {
					setTooltip(null);
					setText(null);
				} else {
					Tooltip tooltip = new Tooltip();
					DataModel myModel = getTableView().getItems().get(getTableRow().getIndex());
					tooltip.setText(myModel.getTip());
					setTooltip(tooltip);
					setText(t.toString());
				}
			}
		};
	}
});

To add tooltips to column cells we have to set a custom cell factory for the target column. In our example DataModel is just the TableView's data model, change that to your model's name. Also this example's model returns Object so that's one more thing you will probably need to change over the casts. The getTip() method is there to also show you how to get the TableRow Data Item.

Tooltips on TableRow

myTable.setRowFactory(new Callback<TableView, TableRow>() {
	@Override
	public TableRow call(final TableView p) {
		return new TableRow() {
			@Override
			public void updateItem(DataModel item, boolean empty) {
				super.updateItem(item, empty);
				if (item == null) {
					setTooltip(null);
				} else {
					Tooltip tooltip = new Tooltip();
					tooltip.setText(getItem().getTip());
					setTooltip(tooltip);
				}
			}
		};
	}
});

Notice that for rows we need to set our custom row factory to the target table not column! Also it's much simpler to get the DataModel when working on rows, you only need to call getItem() method. 

Recently i started working on a JavaFX 2.x project and i needed a lot of TableViews for one of my stages. The most common and best practice to do this, is to implement a class that defines the data model and provides methods and fields to further work with the table. In my case defining 8++ Data Models seemed a little tedious so i started looking for a dynamic way to populate TableViews. I came accross this solution but that too seemed complicated. 

My solution is to create a generic Data Model which can by extended to add as many columns as you want, so not 100% dynamic then. The constructor works with a variant number of parameters so you don't have to create as many constructors as your fields, but you have to create as many getters as your fields. The second part, can be avoided but as long as you can auto generate them in any decent IDE there is no reason to go mad with reflections.

import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TableRow {

    private Object one;
    private Object two;
    private Object three;
    private Object four;
    private Object five;
    private Object six;

    public TableRow(Object... args) {
        Field[] fields = getClass().getDeclaredFields();
        int i = 0;
        for (Object arg : args) {
            try {
                fields[i++].set(this, arg);
            } catch (IllegalArgumentException | IllegalAccessException ex) {
                Logger.getLogger(TableRow.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    /**
     * @return the one
     */
    public Object getOne() {
        return one;
    }

    /**
     * @return the two
     */
    public Object getTwo() {
        return two;
    }

    /**
     * @return the three
     */
    public Object getThree() {
        return three;
    }

    /**
     * @return the four
     */
    public Object getFour() {
        return four;
    }

    /**
     * @return the five
     */
    public Object getFive() {
        return five;
    }

    /**
     * @return the six
     */
    public Object getSix() {
        return six;
    }
}

Then use the model just like in every other tutorial arround the web and the Oracled Docs. In my case it was something like this. Simple, generic and not overly complicated.

private TableView<TableRow> myTable;
@FXML
private TableColumn<TableRow, Object> myTableColumnOne;
@FXML
private TableColumn<TableRow, Object> myTableColumnTwo;

public void populateMyTable()
{
	myTableColumnOne.setCellValueFactory(new PropertyValueFactory<TableRow, Object>("one"));
	myTableColumnTwo.setCellValueFactory(new PropertyValueFactory<TableRow, Object>("two"));

	ObservableList data = FXCollections.observableArrayList();
	for(Map.Entry<String, Integer> entry: myHashMap.entrySet())
	{
		data.add(new TableRow(entry.getKey(), entry.getValue()));
	}
	myTable.getItems().setAll(data);
}

I hope, you will benefit from this solution, leave a comment if you have an idea on how to improve it even further.

javaFXTableViews.png

Edit: 4/25/20013
--------------------------------------------------

I took it on step further to remove the reflection in the constructor.


import java.util.ArrayList;
import java.util.Arrays;

/**
 * Generic data model is a wrapper for ArrayList to easily populate tableViews
 * and other javafx controls. This class would be perfect if java reflection
 * supported magic getters and setters like php or if there was a nice way to
 * dynamically add them during runtime.
 */
public class GenericModel {

    /**
     * The indexed list of objects
     */
    private ArrayList data;

    /**
     * Public constructor with variable number of parameters.
     *
     * @param args
     */
    public GenericModel(Object... args) {
        data = new ArrayList<>(Arrays.asList(args));
    }

    public Object get0() {
        return data.get(0);
    }

    public Object get1() {
        return data.get(1);
    }

    public Object get2() {
        return data.get(2);
    }

    public Object get3() {
        return data.get(3);
    }

    public Object get4() {
        return data.get(4);
    }

    public Object get5() {
        return data.get(5);
    }
}