我第一次使用JavaFX Combobox ...,并且组合框中有2000多个图标(可以通过我从StackOverflow找到的AutoCompleteComboBoxListener进行过滤)。
我打算使用ExecutorService从zip文件中获取图像。
现在,问题在于如何找出组合框中当前可见的项目?
我正在为ComboBox设置自定义ListCellFactory,并且有一个自定义ListCell类,该类也显示图标。我可以以某种方式从ListCell对象中检查项目“是否正在显示”吗?
谢谢。
首先请注意,如果要从单个文件而不是从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] 删除。
我来说两句