【问题标题】:making jfilechooser show image thumbnails使 jfilechooser 显示图像缩略图
【发布时间】:2011-05-05 00:43:36
【问题描述】:

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

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

有没有更有效的选择?欢迎任何建议/指针。

谢谢, 标记

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
}

【问题讨论】:

    标签: java thumbnails jfilechooser


    【解决方案1】:

    我很惊讶地发现,尽管在 Windows 中使用了本机外观,但文件选择器确实没有缩略图视图。我试过你的例子,你走的是正确的路线,但我看到它对于有很多大图像的文件夹有多慢。开销当然是由于读取文件内容然后解释图像时的 I/O,这是不可避免的。

    更糟糕的是,我发现 FileView.getIcon(File) 被称为 lot - 在文件列表显示之前、鼠标悬停在图标上以及选择更改时。如果我们在加载图像后不缓存它们,我们将一直在毫无意义地重新加载图像。

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

    我经常使用ImageImageIcon,我发现ImageIcon 的图像可以随时通过调用setImage(Image) 来更改。这对我们来说意味着,在getIcon(File) 中,我们可以立即返回一个空白或默认图标,但保留对它的引用,将其传递给工作线程,该工作线程将在后台加载图像并稍后设置图标的图像完成后(唯一的问题是我们必须调用 repaint() 才能看到更改)。

    对于这个例子,我使用ExecutorService 缓存线程池(这是获取所有图像的最快方式,但使用大量 I/O)来处理图像加载任务。我还使用WeakHashMap 作为缓存,以确保我们只在需要时保留缓存的图标。您可以使用另一种地图,但您必须管理您持有的图标数量,以避免内存不足。

    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) 缩放时我们不保持图像的纵横比。这样做可能会导致图标尺寸奇怪,从而破坏列表视图的对齐方式。解决方案可能是创建一个 16x16 的新 BufferedImage,并在其顶部居中渲染缩放图像。如果你愿意,你可以实现它!

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

    【讨论】:

      【解决方案2】:

      使用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);
      

      【讨论】:

      • 在 2012 年的 Swing 问题中建议 AWT?哇…
      【解决方案3】:

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

      不确定单个 SwingWorker 是否可以解决问题,或者为每个加载的图标使用一个是否更好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-06
        • 2014-03-23
        • 1970-01-01
        • 2012-04-18
        • 2016-06-13
        • 1970-01-01
        • 2015-09-13
        • 1970-01-01
        相关资源
        最近更新 更多