如何在java swing中延迟加载图像?

问题描述 投票:0回答:1

我想将多个图像加载到

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

提前非常感谢您的帮助!

java swing lazy-loading
1个回答
0
投票

使用 MadProgrammer 提供的链接中的一些建议,我修改了上面链接中找到的代码以进行延迟加载。

基本变化是:

  1. 最初使用包含文件但包含空图标的缩略图对象加载 ListModel
  2. 创建一个 Set 来包含所有要加载的文件
  3. 将 ChangeListener 添加到滚动窗格的视口
  4. 当调用 ChangeListener 时,我会获取当前在视口中可见的图像的所有索引。然后我检查每个图像的文件以查看是否需要加载。如果是这样,我调用 ThumbnailWorker 来加载图像(并将其从要加载的文件集中删除)。

以下是新课程:

缩略图应用程序

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。

© www.soinside.com 2019 - 2024. All rights reserved.