JavaFx Combobox延迟加载图像

用户名

我第一次使用JavaFX Combobox ...,并且组合框中有2000多个图标(可以通过我从StackOverflow找到的AutoCompleteComboBoxListener进行过滤)。

我打算使用ExecutorService从zip文件中获取图像。

现在,问题在于如何找出组合框中当前可见的项目?

我正在为ComboBox设置自定义ListCellFactory,并且有一个自定义ListCell类,该类也显示图标。我可以以某种方式从ListCell对象中检查项目“是否正在显示”吗?

谢谢。

詹姆斯·D

首先请注意,如果要从单个文件而不是从zip文件加载图像,则有一种机制可以避免直接使用任何类型的线程:

ComboBox<MyDataType> comboBox = new ComboBox<>();
comboBox.setCellFactory(listView -> new ListCell<MyDataType>() {

    private ImageView imageView = new ImageView();

    @Override
    public void updateItem(MyDataType item, boolean empty) {
        super.updateItem(item, empty);
        if (empty) {
            setGraphic(null);
        } else {
            String imageURL = item.getImageURL();
            Image image = new Image(imageURL, true); // true means load in background
            imageView.setImage(image);
            setGraphic(imageView);
        }
    }
});

不幸的是,如果您从zip文件加载,我认为您不能使用它,因此您需要创建自己的后台任务。如果单元格中的项目在加载过程中发生更改(如果用户滚动很多,则很有可能),您只需要小心一点就可以确保不使用在后台加载的图像。

(更新说明:我将其更改为侦听itemProperty()单元格中的更改,而不是使用updateItem(...)方法。updateItem(...)与单元格显示的实际项更改时相比,可以更频繁地调用方法,因此该方法避免了不必要的“重载”图片。)

就像是:

ExecutorService exec = ... ;
ComboBox<MyDataType> comboBox = new ComboBox<>();
comboBox.setCellFactory(listView -> {

    ListCell<MyDataType> cell = new ListCell<MyDataType>() ;

    ImageView imageView = new ImageView();
    ObjectProperty<Task<Image>> loadingTask = new SimpleObjectProperty<>();

    cell.emptyProperty().addListener((obs, wasEmpty, isNotEmpty) -> {
        if (isNowEmpty) {
            cell.setGraphic(null);
            cell.setText(null);
        } else {
            cell.setGraphic(imageView);
        }
    });

    cell.itemProperty().addListener((obs, oldItem, newItem) ->  {
        if (loadingTask.get() != null && 
            loadingTask.get().getState() != Worker.State.SUCCEEDED &&
            loadingTask.get().getState() != Worker.State.FAILED) {

            loadingTask.get().cancel();
        }
        loadingTask.set(null) ;
        if (newItem != null) {
            Task<Image> task = new Task<Image>() {
                @Override
                public Image call() throws Exception {
                    Image image = ... ; // retrieve image for item
                    return image ;
                }
            };
            loadingTask.set(task);
            task.setOnSucceeded(event -> imageView.setImage(task.getValue()));
            exec.submit(task);

            cell.setText(...); // some text from item...
        }
    }); 

    return cell ;
});

有关此处的性能的一些想法:

首先,“虚拟化”机制ComboBox意味着将仅创建这些单元格的一小部分,因此您不必担心立即启动成千上万的线程来加载图像,或者实际上您将拥有内存中有成千上万张图像。

当用户滚动列表时,itemProperty(...)由于单元格可用于新项目,因此更改可能会非常频繁。重要的是要确保在项目再次更改之前,不要使用已启动但未完成的线程中的图像。这是在项目更改侦听器的开头取消现有任务的目的。取消任务将阻止onSucceeded处理程序被调用。但是,您仍将运行那些线程,因此,如果可能,call()方法的实现应检查该isCancelled()标志,并在该标志返回true时尽快中止。这可能很难实现:我将先进行实验,看看它如何与简单的实现方式一起工作。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章