使用原生的 Windows 外观,我惊讶地发现文件选择器实际上没有缩略图视图。我尝试了你的例子,你的方向是对的,但我发现在有很多大型图像的文件夹中速度很慢。这当然是由于读取文件内容和解释图像时的 I/O 导致的开销,这是不可避免的。
更糟糕的是,我发现在显示文件列表之前、鼠标悬停在图标上时以及选择更改时会频繁调用 FileView.getIcon(File)
。如果我们不在加载后缓存图像,那么我们将一直无意义地重新加载图像。
显而易见的解决方案是将所有图像加载推迟到另一个线程或线程池中,并一旦得到缩小后的结果,就将其放入临时缓存中,以便可以再次检索它们。
我对 Image
和 ImageIcon
进行了大量尝试,发现 ImageIcon
的图像可以随时通过调用 setImage(Image)
更改。这对我们来说意味着,在 getIcon(File)
中,我们可以立即返回空或默认图标,但保留对它的引用,并将其传递给工作线程,在后台加载图像并在完成时设置图标的图像(唯一需要注意的是,我们必须调用 repaint()
才能看到更改)。
对于此示例,我正在使用一个 ExecutorService
缓存线程池(这是获取所有图像的最快方法,但使用了大量 I/O)来处理图像加载任务。我还使用了 WeakHashMap
作为缓存,以确保我们只保留所需时间的缓存图标。您可以使用另一种类型的 Map,但您必须管理所持有的图标数量,以避免内存不足。
package guitest;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Pattern;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.filechooser.FileView;
public class ThumbnailFileChooser extends JFileChooser {
private static final int ICON_SIZE = 16;
private static final Image LOADING_IMAGE = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB);
private final Pattern imageFilePattern = Pattern.compile(".+?\\.(png|jpe?g|gif|tiff?)$", Pattern.CASE_INSENSITIVE);
private final Map imageCache = new WeakHashMap();
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
JFileChooser chooser = new ThumbnailFileChooser();
chooser.showOpenDialog(null);
System.exit(1);
}
public ThumbnailFileChooser() {
super();
}
{
setFileView(new ThumbnailView());
}
private class ThumbnailView extends FileView {
private final ExecutorService executor = Executors.newCachedThreadPool();
public Icon getIcon(File file) {
if (!imageFilePattern.matcher(file.getName()).matches()) {
return null;
}
synchronized (imageCache) {
ImageIcon icon = imageCache.get(file);
if (icon == null) {
icon = new ImageIcon(LOADING_IMAGE);
imageCache.put(file, icon);
executor.submit(new ThumbnailIconLoader(icon, file));
}
return icon;
}
}
}
private class ThumbnailIconLoader implements Runnable {
private final ImageIcon icon;
private final File file;
public ThumbnailIconLoader(ImageIcon i, File f) {
icon = i;
file = f;
}
public void run() {
System.out.println("Loading image: " + file);
ImageIcon newIcon = new ImageIcon(file.getAbsolutePath());
Image img = newIcon.getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_SMOOTH);
icon.setImage(img);
SwingUtilities.invokeLater(new Runnable() {public void run() {repaint();}});
}
}
}
已知问题:
1) 当缩放图片时,我们不保持图像的长宽比。 这样做可能会导致图标具有奇怪的尺寸,从而破坏列表视图的对齐。 解决方法可能是创建一个新的16x16 BufferedImage
,并在其上绘制缩放后的图像,在中心位置上进行。 如果您愿意,您可以实现这个解决方法!
2) 如果一个文件不是图像,或者损坏了,将根本不会显示任何图标。 程序似乎只在渲染图像时检测到此错误,而不是在加载或缩放图像时检测到,因此我们无法提前检测到这个错误。 但是,如果我们解决第一个问题,可能会检测到它。