Creating jfilechooser show image thumbnails

I wanted to create a JFileChooser with a miniature representation of image files. Therefore, I have subclassed FileView and in the method that creates ImageIcon , some scaling is displayed, so that thumbnails are displayed.

However, the overall effect is that the filechooser widget takes some time before opening the directory and showing thumbnails. Below in createImageIcon () I need to call the new ImageIcon () twice - once using the image file path and next to the modified image as an argument to the constructor. I think this is what slows down the widget.

Is there a more effective alternative? Any suggestions / pointers are most welcome.

thank you sign

 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 } 
+6
java thumbnails jfilechooser
source share
3 answers

I was really surprised to see this, despite the fact that it uses the look and look in Windows, the file selection really has no thumbnails. I tried your example and you are on the right line, but I see how slow it was for folders with lots of large images. The overhead, of course, is associated with I / O when reading the contents of the file, and then with the interpretation of the image, which is inevitable.

What's worse is that I found that FileView.getIcon(File) is called a lot - before displaying a list of files, when you hover over the icon and when the selection changes. If we do not cache the images after they are downloaded, we will uselessly reload the images all the time.

The obvious solution is to pull the entire image load onto another thread or thread pool, and as soon as we get our shortened result, put it in a temporary cache so that it can be retrieved again.

I played a lot with Image and ImageIcon , and I found that ImageIcon image can be changed at any time by calling setImage(Image) . For us, this means that in getIcon(File) we can immediately return an empty or default icon, but keep a link to it, passing it to a workflow that will load the image in the background and set the icon image later when it was done (the only catch we need to call repaint() to see the change).

In this example, I use the thread pool ExecutorService cached streams (this is the fastest way to get all images, but uses a lot of I / O) to handle image loading tasks. I also use WeakHashMap as a cache so that we only keep cached icons for as long as we need them. You can use a different kind of Map, but you will need to control the number of icons you hold on to avoid running out of memory.

 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 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();}}); } } } 

Known Issues:

1) We do not support image aspect ratio when scaling. This can lead to strange sized icons that disrupt the alignment of the list view. Perhaps the solution is to create a new BufferedImage that is 16x16 and displays a scaled image on top of it, centered. You can implement this if you want!

2) If the file is not an image or damaged, the icon will not be displayed at all. It seems that the program detects this error only when rendering the image, and not when loading or scaling, so we can not detect it in advance. However, we can detect this if we fix problem 1.

+7
source share

Use fileDialog instead of JfileChooser to select an image:

 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); 
+5
source share

You can use the default icon for each file and load the actual icons into a different stream (perhaps using SwingWorker?). When the icons load, SwingWorker can call back and refresh the FileView.

Not sure if one SwingWorker will do the trick or it would be better to use it for every icon loaded.

0
source share

All Articles