我想用图像文件的缩略图视图创建一个JFileChooser
。所以我将FileView子类化,并在创建ImageIcon
的方法中进行了一些缩放,以显示缩略图。
然而,总体效果是,filechooser
窗口小部件在打开目录并显示缩略图之前需要一些时间。在下面的createImageIcon()中,我需要使用图像调用两次新的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
}
答案 0 :(得分:7)
我真的很惊讶地发现,尽管使用原生的外观&感觉在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 {
/** 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,我们可能会检测到它。
答案 1 :(得分:5)
使用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);
答案 2 :(得分:0)
您可以为每个文件使用默认图标,并在另一个线程中加载实际图标(可能使用SwingWorker?)。随着图标的加载,SwingWorker可以回调并更新FileView。
不确定单个SwingWorker是否可以执行此操作,或者是否更好地为每个正在加载的图标使用一个。