Issue
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 usingsetGraphic
with the item as parameter. This adds theNode
to the scene inside theTableCell
. - Otherwise convert the object to text using it's
toString
method and usesetText
to display the resulting value as text in theTableCell
.
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.
Answered By - fabian
Answer Checked By - Senaida (JavaFixing Volunteer)