I want to load multiple images into a JPanel
, and I prefer to load them on demand rather than all at once, especially since I'm using JScrollPane
. Most of the images I load are from direct URLs, which I store in an array
of Strings
. I load them one by one in a for loop
using my ImageLoader
method
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();
}
I tried to use imagesPanel.isDisplayable
and expected it to load the images only when they're visible in the JScrollPane
, but it didn't work, and all the images still load simultaneously, which freezes the application. Most of the images I load are 10-50 KBs, so when I load 20 images, it doesn't freeze, but when I load 100, it freezes.
Here is the ImageLoader
class used to load the images.
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();
}
}
Thank you so much in advance for your assistance!