Search code examples
imagejavafxtableviewobservablecollection

JavaFX Tableview displaying Image@Hashcode rather than the Image itself from display model


I'm trying to display an Image for an object in a table view, I have created a DisplayModel class in order to display the relevant values in the table columns from an observable collection, I have 2 columns containing string values and one integer all of which are displaying fine, I also have an image of the Car I wish to display but it will only display the Image@Hashcode rather than the image itself.

The implementation I currently have is as follows:

The display model class:

private ObjectProperty<Image> image;

private SimpleStringProperty make;

private SimpleStringProperty model;

private SimpleDoubleProperty price;

public DisplayModel(Image image, String make, String model, Double price) {
    this.image = new SimpleObjectProperty<>(image);
    this.make = new SimpleStringProperty(make);
    this.model = new SimpleStringProperty(model);
    this.price = new SimpleDoubleProperty(price);
}

public Image getImage() {

    return image.get();
}

public void setImage(Image displayImage) {

    this.image = new SimpleObjectProperty<>(displayImage);
}

The implementation in the initialise method on the controller for the FXML file:

  try {
                OutputStream targetFile = new FileOutputStream(s + "\\" + imageUrl);
                targetFile.write(fileBytes);
                targetFile.close();
                File f = new File(s + imageUrl);
                image = new Image(f.toURI().toString());
            } catch (IOException e) {
                e.printStackTrace();
            }

Using the ImageView controller and trying to display the same image presents it fine so I'm wondering whether it's do with the ObjectProperty implementation or the TableView control.. I've recently faced a similar and far more common issue of needing to override the toString method to return the actual value rather than the String@HashValue, I wonder whether there is a similar way to resolve this issue.

Any advice would be greatly appreciated, thanks for your time.


Solution

  • This has to do with the updateItem implementation of the TableCell returned by the default cellFactory. This is implemented as follows (ignoring empty cells):

    • If the item is a Node, display it using setGraphic with the item as parameter. This adds the Node to the scene inside the TableCell.
    • Otherwise convert the object to text using it's toString method and use setText to display the resulting value as text in the TableCell.

    Image is not a subclass of Node; therefore you get the latter behavior.

    To change this behavior, you need to use a custom cellFactory that deals with the item type properly:

    ItemImage.setCellFactory(col -> new TableCell<Item, Image>() { // assuming model class named Car here; type parameters match the TableColumn
    
        private final ImageView imageView = new ImageView();
        
        {
            // set size of ImageView
            imageView.setFitHeight(50);
            imageView.setFitWidth(80);
            imageView.setPreserveRatio(true);
        
            // display ImageView in cell
            setGraphic(imageView);
        }
        
        @Override
        protected void updateItem(Image item, boolean empty) {
            super.updateItem(item, empty);
            imageView.setImage(item);
        }
    
    });
    

    Note that usually you don't keep the graphic for empty cells. I decided to do this in this case to keep the cell size consistent.