我想将多个图像加载到
JPanel
中,并且我更喜欢按需加载它们而不是一次性加载它们,特别是因为我正在使用JScrollPane
。我加载的大多数图像都来自直接 URL,我将其存储在 array
的 Strings
中。我使用我的 for loop
方法将它们一一加载到
ImageLoader
中
private void loadOnlineImages(String[] images, int maxWidth, int maxHeight) {
imagesPanel.removeAll();
imagesPanel.setLayout(new WrapLayout(FlowLayout.LEFT));
footerPanel.setTotalItems(images.length);
for (String image : images) {
ImageLoader onlineImageLoader =
new ImageLoader(imagesPanel, footerPanel, image, maxWidth, maxHeight);
onlineImageLoader.loadImage();
}
imagesPanel.revalidate();
imagesPanel.repaint();
}
我尝试使用
imagesPanel.isDisplayable
并期望它仅在图像在 JScrollPane
中可见时加载图像,但它不起作用,并且所有图像仍然同时加载,这会冻结应用程序。我加载的大多数图像都是 10-50 KB,所以当我加载 20 个图像时,它不会冻结,但当我加载 100 个图像时,它会冻结。
这是用于加载图像的
ImageLoader
类。
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.swing.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URI;
import java.util.Iterator;
public class ImageLoader {
ProgressListener progressListener;
JPanel footerPanel;
JPanel imagesPanel;
String imageUrl;
int maxWidth;
int maxHeight;
public ImageLoader(JPanel imagesPanel, JPanel footerPanel, String imageUrl, int maxWidth, int maxHeight) {
this.imagesPanel = imagesPanel;
this.imageUrl = imageUrl;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;
this.footerPanel = footerPanel;
progressListener = new ProgressListener(footerPanel);
}
// Load the image only when it becomes visible
public void loadImage() {
new Thread(() -> {
try {
URI uri = URI.create(imageUrl);
ImageInputStream imageInputStream = ImageIO.createImageInputStream(uri.toURL().openStream());
Iterator<ImageReader> iterator = ImageIO.getImageReaders(imageInputStream);
if (iterator.hasNext()) {
ImageReader reader = iterator.next();
reader.setInput(imageInputStream);
reader.addIIOReadProgressListener(progressListener);
BufferedImage image = reader.read(reader.getMinIndex());
final ImageIcon icon = new ImageIcon(image);
// Check if the image is still required to be shown
if (imagesPanel.isDisplayable()) {
SwingUtilities.invokeLater(() -> {
JLabel imageLabel = new JLabel(IconScaler.createScaledIcon(icon, maxWidth, maxHeight));
imagesPanel.add(imageLabel);
imagesPanel.revalidate();
imagesPanel.repaint();
});
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
提前非常感谢您的帮助!
使用 MadProgrammer 提供的链接中的一些建议,我修改了上面链接中找到的代码以进行延迟加载。
基本变化是:
以下是新课程:
缩略图应用程序
import java.io.*;
import java.util.concurrent.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class ThumbnailApp
{
private DefaultListModel<Thumbnail> model = new DefaultListModel<Thumbnail>();
private JList<Thumbnail> list = new JList<Thumbnail>(model);
private Set<File> filesToBeLoaded = new HashSet<>();
private ExecutorService service;
public ThumbnailApp()
{
int processors = Runtime.getRuntime().availableProcessors();
service = Executors.newFixedThreadPool( processors - 2 );
}
public JPanel createContentPane()
{
JPanel cp = new JPanel( new BorderLayout() );
list.setCellRenderer( new ThumbnailRenderer<Thumbnail>() );
list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
list.setVisibleRowCount(-1);
Icon empty = new EmptyIcon(160, 160);
Thumbnail prototype = new Thumbnail(new File("PortugalSpain-000.JPG"), empty);
list.setPrototypeCellValue( prototype );
JScrollPane scrollPane = new JScrollPane( list );
cp.add(scrollPane, BorderLayout.CENTER);
scrollPane.getViewport().addChangeListener((e) ->
{
int first = list.getFirstVisibleIndex();
int last = list.getLastVisibleIndex();
System.out.println(first + " : " + last);
if (first == -1) return;
for (int i = first; i <= last; i++)
{
Thumbnail thumbnail = model.elementAt(i);
File file = thumbnail.getFile();
if (filesToBeLoaded.contains(file))
{
filesToBeLoaded.remove(file);
service.submit( new ThumbnailWorker(thumbnail.getFile(), model, i) );
}
}
if (filesToBeLoaded.isEmpty())
service.shutdown();
});
return cp;
}
public void loadImages(File directory)
{
new Thread( () -> createThumbnails(directory) ).start();
}
private void createThumbnails(File directory)
{
try
{
File[] files = directory.listFiles((d, f) -> {return f.endsWith(".JPG");});
for (File file: files)
{
filesToBeLoaded.add( file );
Thumbnail thumbnail = new Thumbnail(file, null);
model.addElement( thumbnail );
}
}
catch(Exception e) { e.printStackTrace(); }
}
private static void createAndShowGUI()
{
ThumbnailApp app = new ThumbnailApp();
JFrame frame = new JFrame("ListDrop");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane( app.createContentPane() );
frame.setSize(1600, 900);
frame.setVisible(true);
// File directory = new File("C:/Users/netro/Pictures/TravelSun/2019_01_Cuba");
File directory = new File("C:/Users/netro/Pictures/TravelAdventures/2018_PortugalSpain");
app.loadImages( directory );
}
public static void main(String[] args)
{
javax.swing.SwingUtilities.invokeLater(() -> createAndShowGUI());
}
}
缩略图
import java.io.File;
import javax.swing.Icon;
public class Thumbnail
{
private File file;
private Icon icon;
public Thumbnail(File file, Icon icon)
{
this.file = file;
this.icon = icon;
}
public Icon getIcon()
{
return icon;
}
public void setIcon(Icon icon)
{
this.icon = icon;
}
public File getFile()
{
// return file.getName();
return file;
}
}
缩略图工作者
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;
public class ThumbnailWorker extends SwingWorker<Image, Void>
{
private File file;
private DefaultListModel<Thumbnail> model;
private int index;
public ThumbnailWorker(File file, DefaultListModel<Thumbnail> model, int index)
{
this.file = file;
this.model = model;
this.index = index;
}
@Override
protected Image doInBackground() throws IOException
{
// Image image = ImageIO.read( file );
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("jpg");
ImageReader reader = readers.next();
ImageReadParam irp = reader.getDefaultReadParam();
// irp.setSourceSubsampling(10, 10, 0, 0);
irp.setSourceSubsampling(5, 5, 0, 0);
ImageInputStream stream = new FileImageInputStream( file );
reader.setInput(stream);
Image image = reader.read(0, irp);
int width = 160;
int height = 90;
if (image.getHeight(null) > image.getWidth(null))
{
width = 90;
height = 160;
}
BufferedImage scaled = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = scaled.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
image = null;
return scaled;
}
@Override
protected void done()
{
try
{
ImageIcon icon = new ImageIcon( get() );
Thumbnail thumbnail = model.get( index );
thumbnail.setIcon( icon );
model.set(index, thumbnail);
// System.out.println("finished: " + file);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
缩略图渲染器
import java.awt.Component;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class ThumbnailRenderer<E> extends JLabel implements ListCellRenderer<E>
{
public ThumbnailRenderer()
{
setOpaque(true);
setHorizontalAlignment(CENTER);
setVerticalAlignment(CENTER);
setHorizontalTextPosition( JLabel.CENTER );
setVerticalTextPosition( JLabel.BOTTOM );
setBorder( new EmptyBorder(4, 4, 4, 4) );
}
/*
* Display the Thumbnail Icon and file name.
*/
public Component getListCellRendererComponent(JList<? extends E> list, E value, int index, boolean isSelected, boolean cellHasFocus)
{
if (isSelected)
{
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
}
else
{
setBackground(list.getBackground());
setForeground(list.getForeground());
}
//Set the icon and filename
Thumbnail thumbnail = (Thumbnail)value;
setIcon( thumbnail.getIcon() );
setText( thumbnail.getFile().getName() );
// System.out.println(thumbnail.getFileName());
return this;
}
}
空图标
import java.awt.*;
import javax.swing.*;
public class EmptyIcon implements Icon
{
private int width;
private int height;
public EmptyIcon(int width, int height)
{
this.width = width;
this.height = height;
}
public int getIconWidth()
{
return width;
}
public int getIconHeight()
{
return height;
}
public void paintIcon(Component c, Graphics g, int x, int y)
{
}
}
一旦 filesToBeLoaded 集为空,您可能还需要考虑从视口中删除 ChangeListener。