使jfilechooser显示图像缩略图

我想创建一个带有图像文件缩略图视图的JFileChooser 。所以我将FileView子类化,并在创建ImageIcon的方法中做了一些缩放,以显示缩略图。

但是,整体效果是, filechooser小部件在打开目录并显示缩略图之前需要一些时间。在下面的createImageIcon()中,我需要使用图像文件路径两次调用新的ImageIcon(),然后使用resize的图像作为构造函数参数。我认为这是减慢小部件的速度。

有没有更有效的替代方案?任何建议/指针都是最受欢迎的。

谢谢,马克

 public static void main(String[] args) { JFileChooser chooser=new JFileChooser(); ThumbNailView thumbView=new ThumbNailView(); chooser.setFileView(thumbView); } class ThumbNailView extends FileView{ public Icon getIcon(File f){ Icon icon=null; if(isImageFile(f.getPath())){ icon=createImageIcon(f.getPath(),null); } return icon; } private ImageIcon createImageIcon(String path,String description) { if (path != null) { ImageIcon icon=new ImageIcon(path); Image img = icon.getImage() ; Image newimg = img.getScaledInstance( 16, 16, java.awt.Image.SCALE_SMOOTH ) ; return new ImageIcon(newimg); } else { System.err.println("Couldn't find file: " + path); return null; } } private boolean isImageFile(String filename){ //return true if this is image } 

我实际上很惊讶地看到,尽管在Windows中使用了原生外观,但文件选择器确实没有缩略图视图。 我尝试了你的例子,你沿着正确的方向前进,但我看到有很多大图像的文件夹有多慢。 当然,开销是由于读取文件内容然后解释图像时的I / O,这是不可避免的。

更糟糕的是,我发现FileView.getIcon(File)大量调用 – 在显示文件列表之前,当鼠标hover在图标上以及选择发生变化时。 如果我们在加载后不缓存图像,我们将无意义地重新加载图像。

显而易见的解决方案是将所有图像加载推送到另一个线程或线程池,一旦我们得到缩小的结果,将其放入临时缓存中,以便可以再次检索它。

我经常使用ImageImageIcon ,我发现可以通过调用setImage(Image)随时更改ImageIcon的图像。 这对我们来说意味着,在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 { /** All preview icons will be this width and height */ private static final int ICON_SIZE = 16; /** This blank icon will be used while previews are loading */ private static final Image LOADING_IMAGE = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB); /** Edit this to determine what file types will be previewed. */ private final Pattern imageFilePattern = Pattern.compile(".+?\\.(png|jpe?g|gif|tiff?)$", Pattern.CASE_INSENSITIVE); /** Use a weak hash map to cache images until the next garbage collection (saves memory) */ 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(); } // --- Override the other constructors as needed --- { // This initializer block is always executed after any constructor call. setFileView(new ThumbnailView()); } private class ThumbnailView extends FileView { /** This thread pool is where the thumnnail icon loaders run */ private final ExecutorService executor = Executors.newCachedThreadPool(); public Icon getIcon(File file) { if (!imageFilePattern.matcher(file.getName()).matches()) { return null; } // Our cache makes browsing back and forth lightning-fast! :D synchronized (imageCache) { ImageIcon icon = imageCache.get(file); if (icon == null) { // Create a new icon with the default image icon = new ImageIcon(LOADING_IMAGE); // Add to the cache imageCache.put(file, icon); // Submit a new task to load the image and update the 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); // Load and scale the image down, then replace the icon's old image with the new one. ImageIcon newIcon = new ImageIcon(file.getAbsolutePath()); Image img = newIcon.getImage().getScaledInstance(ICON_SIZE, ICON_SIZE, Image.SCALE_SMOOTH); icon.setImage(img); // Repaint the dialog so we see the new icon. SwingUtilities.invokeLater(new Runnable() {public void run() {repaint();}}); } } } 

已知的问题:

1)缩放时我们不保持图像的宽高比。 这样做可能会导致图标具有奇怪的尺寸,从而破坏列表视图的对齐方式。 解决方案可能是创建一个16×16的新BufferedImage ,并在其上方渲染缩放图像,居中。 如果你愿意,你可以实现!

2)如果文件不是图像或已损坏,则根本不会显示任何图标。 看起来程序只在渲染图像时检测到这个错误,而不是在我们加载或缩放它时,所以我们无法提前检测到。 但是,如果我们解决问题1,我们可能会检测到它。

使用fileDialog而不是JfileChooser来选择图像:

 FileDialog fd = new FileDialog(frame, "Test", FileDialog.LOAD); String Image_path fd.setVisible(true); name = fd.getDirectory() + fd.getFile(); image_path=name; ImageIcon icon= new ImageIcon(name); icon.setImage(icon.getImage().getScaledInstance(jLabel2.getWidth(),jLabel2.getHeight() , Image.SCALE_DEFAULT)); jLabel2.setIcon(icon); 

您可以为每个文件使用默认图标,并在另一个线程中加载实际图标(可能使用SwingWorker?)。 随着图标的加载,SwingWorker可以回调并更新FileView。

不确定单个SwingWorker是否会执行此操作,或者是否更好地为每个正在加载的图标使用一个。