1
\$\begingroup\$

This code is based off https://stackoverflow.com/questions/25711103/javafx-combobox-lazy-loading-images but modified to work with a TableCell

Firstly, the model object should have an observable File property which represents the image to load.

We need some background executors

ExecutorService exec = Executors.newCachedThreadPool();

Then in the initializer:

TableColumn<Model, File>= new TableColumn<>("");
colImage.setPrefWidth(64);
colImage.setCellFactory(param -> {

    //Set up the ImageView
    final ImageView imageview = new ImageView();
    imageview.fitWidthProperty().bind(Bindings.subtract(colImage.widthProperty(), 7));
    imageview.setPreserveRatio(true);

    //Loading task
    ObjectProperty<Task<Image>> loadingTask = new SimpleObjectProperty<>();

    //Set up the Table
    TableCell<Model, File> cell = new TableCell<Model, File>() {
        @Override
        public void updateItem(File item, boolean empty) {
            //Stop already running image fetch tast
            if (loadingTask.get() != null &&
                    loadingTask.get().getState() != Worker.State.SUCCEEDED &&
                    loadingTask.get().getState() != Worker.State.FAILED) {

                loadingTask.get().cancel();
            }
            loadingTask.set(null);
            //Load image if not null
            if (empty || item == null) {
                imageview.setVisible(false);
            } else {
                imageview.setVisible(true);
                Task<Image> task = new Task<Image>() {
                    @Override
                    public Image call() throws Exception {
                        System.out.println("fetch image");
                        Image image = SwingFXUtils.toFXImage(ImageIO.read(item), null);
                        return image;
                    }
                };
                loadingTask.set(task);
                task.setOnSucceeded(event -> {
                    imageview.setImage(task.getValue());
                    System.out.println("got image");
                });
                exec.submit(task);
            }
        }
    };
    // Attach the imageview to the cell
    cell.setGraphic(imageview);
    return cell;

});
colImage.setCellValueFactory(cellData -> cellData.getValue().fileProperty());
tableView.getColumns().add(0, colImage);

How it works:

When the file property is updated the updateItem method spawns a Task that loads the image and updates the ImageView. However, TableCells are reused and thus an image might be still loading when updateItem is called again. To prevent this, the Task for that TableCell is stored in a property, and cancelled if it is still running when updateItem is called again.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.