1

First of all, sorry my english and thanks for reading.. :-)

I have a tableview and it show some information about a class named Produto.

The table has a column where is displayed a image with the name of produto, but I need to show this image only for some type of Produto.

The Produto class:

public class Produto {

   private Integer id;
   private String nome;
   private Tipo type;


   //get set..

}

The column of table:

TableColumn<Produto,String> tbcNomeProduto = new TableColumn<Produto,String>();

tbcNomeProduto.setCellValueFactory(new PropertyValueFactory<Produto, String>("nome"));
        tbcNomeProduto.setCellFactory(new Callback<TableColumn<Produto,String>,TableCell<Produto,String>>(){        
            @Override
            public TableCell<Produto, String> call(TableColumn<Produto, String> param) {                
                TableCell<Produto, String> cell = new TableCell<Produto, String>(){



                    @Override
                    public void updateItem(String item, boolean empty) {                        

                        if(item != null){

                            HBox box= new HBox();
                            box.setSpacing(10);
                            VBox vbox = new VBox();
                            vbox.getChildren().add(new Label(item));

                            ImageView imageview = new ImageView();
                            imageview.setImage(new Image(getClass().getResourceAsStream("16x16.png"))); 

                            box.getChildren().addAll(imageview,vbox); 

                            setGraphic(box);
                        }
                    }
                };

                return cell;
            }

        });

How could I access the current Produto object inside the updateItem, to get the type of Produto and choose if display or not the image on the table?

supdown
  • 33
  • 1
  • 3
  • 1
    This is slightly different from [JavaFX 2 TableView : different cell factory depending on the data inside the cell](http://stackoverflow.com/questions/17067481/javafx-2-tableview-different-cell-factory-depending-on-the-data-inside-the-cel), but overall, pretty similar. – jewelsea Jan 13 '15 at 16:04

1 Answers1

1

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.

James_D
  • 201,275
  • 16
  • 291
  • 322