0

I want to create listView item with multiple labels and images. I created a cell class:

public class Cell extends ListCell<String> {
private HBox hBox;
private Label description;
private Pane pane;
private ImageView imageView;
private Image deleteImage;
private JFXButton delete;

public Cell() {
    super();
    this.hBox = new HBox();
    this.description = new Label();
    this.pane = new Pane();
    this.delete = new JFXButton("delete");

    this.hBox.getChildren().addAll(description, pane, delete);
    HBox.setHgrow(pane, Priority.ALWAYS);
    delete.setOnAction(event -> getListView().getItems().remove(getItem()));
}

@Override
protected void updateItem(String item, boolean empty) {
    super.updateItem(item, empty);
    setText(null);
    setGraphic(null);

    if (item != null && !empty) {
        description.setText(item);
        setGraphic(hBox);
    }
}

This is part of my controller code:

 void addToShopCart(ActionEvent event) {
    Image partImage = selectedPart.getPicture();
    shopListView.getItems().addAll(selectedPart.getDescription());
    shopListView.setCellFactory(param -> new Cell(partImage));
}

selectedpart my custom class which has an image. But now I have no idea how to send texts to these labels, because updateItem() method gets only one String. And about image, I want to set different image to each item seperately. I tried to send to constructor, but then all images are the same. I am using MVC and I can also work with code.

  • Only after posting my answer I have noticed this very similar question: https://stackoverflow.com/questions/34838341/javafx-custom-cell-factory-with-custom-objects . Worth having a look (and possibly marking as duplicate). – Itai May 29 '18 at 06:36

1 Answers1

1

It sounds like your list items are more than just strings, and so your model should be a more specialized class. Depending on how complex it is, you may even want to use a TableView, but for now lets assume you only have a name and an image and wish to use a ListView.

First you need a class describing the items in the list - presumably they are "products" (as it is a shopping list) and have a description and an image:

class Product {
    final StringProperty description = new SimpleStringProperty(this, "description"); 
    final StringProperty image = new SimpleStringProperty(this, "image"); 

    // add getters, property-getters and setters...
}

Next, define your Cell class as extending ListCell<Product> - this will mean the item property will be of type Product, as should be the type of the item argument to the updateItem method.
Now whenever an item is updated/changed you can change both the decription and the image:

@Override
protected void updateItem(Product item, boolean empty) {
    super.updateItem(item, empty);
    setText(null);
    setGraphic(null);

    if (item != null && !empty) {
        description.setText(item.getDescription());
        imageView.setImage(...);
        setGraphic(hBox);
    }
}

When adding an item, create a new Product instance with the appropriate description and image and add it to the list. Note that there is no need to call setCellFactory every time an item is added - it should be called once during initialization (hypothetically there may be reasons to change the cell factory after it has been set, but this is not a common practice and is not needed in your case).

Note: loading images may be slow (depending on image size), so you may want to cache or pre-load the actual Image objects. You could also use the same Image object in multiple ImageView instances, so if multiple products use the same image this may also be beneficial.

Itai
  • 6,641
  • 6
  • 27
  • 51
  • 1
    You should also set the `ImageView.image` property to `null` to allow the garbage collector to claim the image instance, if it's no longer referenced somewhere else. Furthermore using `fitHeight` and `fitWidth` would probably be beneficial. *"(hypothetically there may be reasons to change the cell factory after it has been set, but this is not a common practice and is not needed in your case"* This will not work. Already created cells are not recreated, if you swap the factory. This would lead to diffent cell implementations without control over the use... – fabian May 29 '18 at 08:18
  • @fabian My point was you should almost never call `setCellFactory` more than once, although it may in some very rare cases be desirable, which may be why doing so doesn't raise an exception... I can't think of a good reason at hand, but I also can't say for sure there aren't any. – Itai May 29 '18 at 09:09
  • Thank you for answer. The link you gave couldn't help. But your answer could. One more question: can I create FXML file to listItem and use it inside the Cell class? – Qudratxo'ja Musayev May 29 '18 at 09:12
  • See https://stackoverflow.com/questions/19588029/customize-listview-in-javafx-with-fxml – Itai May 29 '18 at 09:20