且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

JList 重绘单个元素

更新时间:2023-02-05 18:20:43

我发现当我使用listModel的fireDataChanged方法时(见下文),列表中的所有可见项都被重新绘制

I find that when I use the listModel's fireDataChanged method (see below), all the visible items in the list are repainted

您不应手动调用该方法.fireXXX(...) 方法只能由模型本身调用.

You should NOT invoke that method manually. The fireXXX(...) methods should only be invoked by the model itself.

您应该使用以下方法更新模型:

You should be updating the model by using the:

model.set(...);

set(...) 方法然后将调用适当的方法来通知 JList 重新绘制单元格.

The set(...) method will then invoke the appropriate method to notify the JList to repaint the cell.

这是我对一个简单的缩略图应用程序的尝试.它尝试通过以下方式增加性能改进:

Here is my attempt at a simple Thumbnail app. It attempts to add performance improvements by:

  1. 使用默认图标加载模型,以便列表不需要不断调整自身大小
  2. 使用 ExecutorService 来利用多个处理器
  3. 使用 ImageReader 读取文件.子采样属性允许您在缩放图像时使用更少的像素.

只需将类更改为指向包含一些 .jpg 文件的目录并试一试:

Just change the class to point to a directory containing some .jpg files and give it a go:

缩略图应用程序:

import java.io.*;
import java.util.concurrent.*;
import java.awt.*;
//import java.awt.datatransfer.*;
import java.awt.event.*;
import javax.swing.*;

class ThumbnailApp
{
    private DefaultListModel<Thumbnail> model = new DefaultListModel<Thumbnail>();
    private JList<Thumbnail> list = new JList<Thumbnail>(model);

    public ThumbnailApp()
    {
    }

    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 );
        cp.add(new JScrollPane( list ), BorderLayout.CENTER);

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

            int processors = Runtime.getRuntime().availableProcessors();
            ExecutorService service = Executors.newFixedThreadPool( processors - 2 );

            long start = System.currentTimeMillis();

            for (File file: files)
            {
                Thumbnail thumbnail = new Thumbnail(file, null);
                model.addElement( thumbnail );
//              new ThumbnailWorker(file, model, model.size() - 1).execute();
                service.submit( new ThumbnailWorker(file, model, model.size() - 1) );
            }

            long duration = System.currentTimeMillis() - start;
            System.out.println(duration);

            service.shutdown();
        }
        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.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.Iterator;
//import java.util.concurrent.*;
import javax.imageio.*;
import javax.imageio.stream.*;
import javax.swing.*;

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.getFileName() );

        return this;
    }
}

缩略图:

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 String getFileName()
    {
        return file.getName();
    }
}

我在一个包含 302 个图像的目录上进行了测试.使用 ExecutorService 将加载时间从 2:31 缩短到 0:35.

I tested on a directory with 302 images. Using the ExecutorService got the load time down from 2:31 to 0:35.