The way I usually approach this is to make the type of the table column the same as the type of the table:
TableColumn<Produto, Produto> tbcNomeProduto = new TableColumn<>();
The reason for this is that the value displayed in the cells in this column depends on two things: Produto.nome
and Produto.type
; in other words the cells in this column are views of both Produto.nome
and Produto.type
. Thus the smallest entity containing all the data needed to display the cell (i.e. the smallest entity of which the cell is a view) is the Produto
instance itself.
So now I would do
tbcNomeProduto.setCellValueFactory(new Callback<CellDataFeatures<Produto, Produto>, ObservableValue<Produto>>() {
@Override
public ObservableValue<Produto> call(CellDataFeatures<Produto, Produto> data) {
return new ReadOnlyObjectWrapper<>(data.getValue());
}
});
tbcNomeProduto.setCellFactory(new Callback<TableColumn<Produto, Produto>, TableCell<Produto, Produto>>() {
@Override
public TableCell<Produto, Produto> call(TableColumn<Produto, Produto> col) {
return new TableCell<Produto, Produto>() {
private HBox hbox = new HBox();
private ImageView imageView = new ImageView(new Image(getClass().getResourceAsStream("16x16.png")));
private Label label = new Label();
// anonymous constructor:
{
setGraphic(hbox);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
@Override
public void updateItem(Produto item, boolean empty) {
super.updateItem(item, empty);
hbox.getChildren().clear();
if (item != null) {
Tipo type = item.getType();
String nome = item.getNome();
if (/* should show image...*/) {
hbox.getChildren().add(imageView);
}
hbox.getChildren().add(label);
}
}
};
}
});
In your example, your Produto
class is a POJO, following the standard JavaBean conventions and not using observable JavaFX Properties. Thus if the nome
field were to change for a displayed Produto
instance, there would be no opportunity for the table to update anyway, as the nome
field is not observable.
If you were to have observable fields in the Produto
class, and those values could change while the object was displayed, then you would need to do a bit more work in the table cell. This is because the nome
field could change without the Produto
instance changing; the latter means that the updateItem(...)
method would not be invoked. I want to include the code to manage this for other readers who may come across this question, even though it's not relevant for the question at hand. Using anonymous inner classes for this gets very ugly in a hurry, so I'm going to revert to Java 8 code and use lambda expressions for this section.
So suppose the Produto
class looks like
public class Produto {
private final StringProperty nome = new SimpleStringProperty();
public StringProperty nomeProperty() {
return nome ;
}
public final String getNome() {
return nomeProperty().get();
}
public final void setNome(String nome) {
nomeProperty().set(nome);
}
private final ObjectProperty<Tipo> type = new SimpleObjectProperty<>() ;
// get/set and property accessor methods as above....
private final IntegerProperty id = new SimpleIntegerProperty();
// get/set and property accessor methods...
}
As above you have
TableColumn<Produto, Produto> tbcNomeProduto = new TableColumn<>();
tbcNomeProduto.setCellValueFactory(cellData -> new ReadOnlyPropertyWrapper<>(cellData.getValue()));
For the cell factory, you need to create listeners for the individual properties. Register and deregister those properties when the Produto
that is displayed changes:
tbc.setCellFactory(col -> {
Label label = new Label();
ImageView imageView = new ImageView(new Image(getClass().getResourceAsStream("16x16.png")));
HBox hbox = new HBox();
TableCell<Produto, Produto> cell = new TableCell<>();
// listener for the nome property changing:
ChangeListener<String> nomeListener = (obs, oldNome, newNome) -> label.setText(newNome);
// listener for type property changing:
ChangeListener<Tipo> typeListener = (obs, oldType, newType) -> {
if ( /* should show image */) {
hbox.getChildren().setAll(imageView, label);
} else {
hbox.getChildren().setAll(label);
}
}
cell.itemProperty().addListener((obs, oldProduto, newProduto) -> {
if (oldProduto != null) {
oldProduto.nomeProperty().removeListener(nomeListener);
oldProduto.typeProperty().removeListener(typeListener);
}
if (newProduto == null) {
cell.setGraphic(null);
} else {
label.setText(newProduto.getNome());
Tipo type = newProduto.getType();
if (/* should show graphic */) {
hbox.getChildren().setAll(imageView, label);
} else {
hbox.getChildren().setAll(label);
}
cell.setGraphic(hbox);
newProduto.nomeProperty().addListener(nomeListener);
newProduto.typeProperty().addListener(typeListener);
}
});
cell.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
return cell ;
});
This generalizes fairly easily to display different images under different conditions, or even to manipulate css styles for the cell, etc.