private static Map<String, Image> images = new HashMap<>();
...
Image image;
if ((image = images.get(item.getOwnerId())) == null) {
img.setImage(null);
new Thread(() -> {
Image image1 = new Image(item.getOwnerProfilePicUrl());
images.put(item.getOwnerId(), image1);
Platform.runLater(() -> img.setImage(image1));
}).start();
} else {
img.setImage(image);
}
缓存图像是一个好主意,但这样做是错误的。加载(&A);将图像插入到其他线程上的地图。由于不同步访问,因此不能保证两个线程以相同的方式查看映射。同时考虑到以下事实:
Image
提供一种异步加载
形象
,实际上没有必要自己创建线程。
此外,对于大量数据,您可能希望删除GUI当前未使用的图像。使用
SoftReference
s是个好主意。
然而,主要问题是缺乏同步。如果滚动速度足够快,多个线程可能会为同一单元格加载不同的图像,并且您不知道最后一个要启动的图像是否是最后一个要执行的图像
Platform.runLater
。可以并行加载来自同一源的多个图像。
也没有办法重用缓存。如果应用程序的其他部分需要这些图像,那么就不可能以这种方式重用它们。
我的建议:
...
import java.awt.Desktop; // importing more classes from awt than neccessary could result in problems
...
public class DischargeComment extends ListCell<Comment> {
...
/**
* Constructor to pass external cache
* @param cache
*/
public DischargeComment(Map<String, SoftReference<Image>> cache) {
if (cache == null) {
throw new IllegalArgumentException();
}
this.cache = cache;
}
/**
* constructor using the default cache
*/
public DischargeComment() {
this(getDefaultCache());
}
private final Map<String, SoftReference<Image>> cache;
private static Map<String, SoftReference<Image>> defaultCache;
private static final URL FXML_URL = DischargeComment.class.getResource("cell/discharge_comment.fxml");
public static Map<String, SoftReference<Image>> getDefaultCache() {
if (defaultCache == null) {
defaultCache = new HashMap<>();
}
return defaultCache;
}
public static String search = "";
private boolean loaded = false; // no need for a reference to fxmlloader here
@Override
protected void updateItem(Comment item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
} else {
if (!loaded) {
FXMLLoader mLLoader = new FXMLLoader(FXML_URL);
mLLoader.setController(this);
try {
mLLoader.load();
img.setClip(new Circle(25, 25, 25));
loaded = true;
} catch (IOException e) {
e.printStackTrace();
}
}
...
// use single access here
// also use url as key
cache.compute(item.getOwnerProfilePicUrl(), (key, value) -> {
Image image = null;
if (value != null) {
image = value.get();
}
if (image == null) {
image = new Image(key, true); // load image in background
value = new SoftReference<>(image);
}
img.setImage(image);
return value;
});
lblUsername.setOnMouseClicked(e->{
try {
Desktop.getDesktop().browse(new URI("https://www.instagram.com/" + item.getOwnerUsername()));
} catch (IOException | URISyntaxException exception) {
exception.printStackTrace();
}
});
setText(null);
setGraphic(pane);
setPrefHeight(Region.USE_COMPUTED_SIZE); // don't set height to -1
}
}
}