Windows でネイティブのルック アンド フィールを使用しているにもかかわらず、実際にはファイル チューザーにサムネイル ビューがないことに驚きました。私はあなたの例を試してみましたが、あなたは正しい方向に進んでいますが、大きな画像がたくさんあるフォルダーではどれだけ遅いかがわかります。もちろん、オーバーヘッドは、ファイルの内容を読み取ってイメージを解釈するときの I/O によるものであり、これは避けられません。
さらに悪いことに、ファイル リストが表示される前、アイコンの上にマウスを置いたとき、および選択が変更されたときに、 が頻繁に呼び出さFileView.getIcon(File)
れることがわかりました。画像を読み込んだ後に画像をキャッシュしないと、無意味に画像を常に再読み込みすることになります。
明らかな解決策は、すべての画像の読み込みを別のスレッドまたはスレッド プールにプッシュし、スケールダウンした結果を一時キャッシュに入れて、再度取得できるようにすることです。
私はImage
と をImageIcon
いろいろいじり、 をImageIcon
呼び出すことでいつでも のイメージを変更できることを発見しましたsetImage(Image)
。これが私たちにとって何を意味するかというとgetIcon(File)
、空白またはデフォルトのアイコンをすぐに返すことができますが、それへの参照を保持し、バックグラウンドで画像をロードし、後で完了時にアイコンの画像を設定するワーカー スレッドに渡します。 (唯一の問題は、変更を確認するために呼び出す必要があることですrepaint()
)。
この例では、ExecutorService
キャッシュされたスレッド プール (すべての画像を取得する最速の方法ですが、多くの I/O を使用します) を使用して画像の読み込みタスクを処理しています。また、キャッシュとしてa を使用し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) スケーリング時に画像の縦横比を維持しません。これを行うと、リスト ビューの配置が崩れる奇妙な寸法のアイコンが生成される可能性があります。BufferedImage
解決策は、おそらく16x16の新しいものを作成し、拡大縮小された画像をその上に中央揃えでレンダリングすることです。希望すれば実装できます!
2) ファイルが画像でない場合、または破損している場合、アイコンはまったく表示されません。プログラムがこのエラーを検出するのは、画像をロードまたはスケーリングするときではなく、画像のレンダリング中にのみであるように見えるため、これを事前に検出することはできません。ただし、問題 1 を修正すれば検出される可能性があります。