2012-11-24 55 views
2

我正在写一个程序部分只是为了好玩,部分是为了帮助我处理一堆数字图片,我想将它们分成打印类别。主要思想是它应该在单个列中显示图片,并在每张图片旁边有一组复选框,并带有类别名称。我检查所需的复选框,按“去!”按钮,图片将被复制到子文件夹中,具体取决于所选的复选框。处理大量的大jpgs

现在,一切都快完成了 - 除了一件事。有问题的照片是大jpgs,每个大约7-8MB,其中大约有700个。如果我试图一次加载它们,自然需要大量的内存和时间来加载它们。那么,这个问题有很好的解决方法吗?我的两个想法如下。

1)一次加载图片10,并在下一个/上一个按钮的地方。我不喜欢这个想法,因为它增加了不必要的元素。 2)使应用程序加载新图片,当你滚动到他们并卸载你滚动过去。我真的喜欢这个主意。

有人能指出我正确的方向,至于如何实现后者的想法?我只在Google上找到一个相关的link,但我不能说它对我有帮助,我对代码的某些部分感到困惑。

+0

*“大jpgs,每个大约7-8MB”*取决于压缩,这可能导致大量的最终像素大小。图像是什么样的WxH? –

+0

@AndrewThompson,约2600 * 3900,相反。 –

回答

2

如果缩略图足够,这个answer包括一个简单的重采样方法,并引用一些权衡。如果没有,这个answer概述了显示和缓存最近图像的一般方法。

在任一情况下,JTable的默认Booleanrenderer/editorJCheckBoxCheckOne就是一个例子。

+0

感谢您的链接,我发现第二个最有用的,我是Swing的新手(实际上Java中的新手很多),所以我甚至都没有想到JTable。 –

+0

如果使用'JTable',默认渲染器也会识别缩略图的'Icon'和'ImageIcon'。 – trashgod

1

你必须为所有图片创建thumnails,你可以保留thumnails在内存中。 这可能需要很长时间。

然后,你要么准备好与会议。或者缩略图不适合所有内存。 如果是这样的话:你加载它们的30-40个,并且在滚动期间你检测滚动方向,并且在另一个线程中加载下一组。

如果负载比用户滚动速度较慢,那么你dispaly默认的占位符图像这样的“尚未加载PIC”

+0

+1我建议缓存缩略图,但这完全是另一个问题;) – MadProgrammer

+0

一个典型的用户界面挂起的图像图标显示[这里](http://stackoverflow.com/a/11092120/230513)。 – trashgod

+0

@AlexWien是的,滚动的某种加载是我试图实现的事情。 –

0

可以缩成所有的图像使用较少的内存。例如此代码将所有图像缩小到200x200。这样你就可以放入100MB的1000张图片。

import javax.swing.*; 
import java.awt.*; 
import java.awt.image.*; 
import javax.imageio.ImageIO; 
import java.io.File; 

public class Scroll extends JPanel { 
    public static void main(String[] args) throws Exception { 
     JFrame frame = new JFrame(); 
     JPanel panel = new Scroll(); 

     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 

     for(int i = 0; i < 10; i++) { 
      JPanel buttonPanel = new JPanel(); 
      JRadioButton b1 = new JRadioButton("button 1"); 
      JRadioButton b2 = new JRadioButton("button 2"); 
      JRadioButton b3 = new JRadioButton("button 3"); 
      ButtonGroup group = new ButtonGroup(); 
      group.add(b1); 
      group.add(b2); 
      group.add(b3); 
      buttonPanel.add(b1); 
      buttonPanel.add(b2); 
      buttonPanel.add(b3); 

      BufferedImage buffer = new BufferedImage(200,200,BufferedImage.TYPE_INT_RGB); 
      Graphics2D g = buffer.createGraphics(); 

      BufferedImage image = ImageIO.read(new File("image.jpg")); 
      g.scale(buffer.getWidth()*1.0/image.getWidth(), 
        buffer.getHeight()*1.0/image.getHeight()); 
      g.drawImage(image, 0, 0, null); 
      g.dispose(); 
      JLabel imageLabel = new JLabel(new ImageIcon(buffer)); 
      JSplitPane splitPane = new JSplitPane(); 
      splitPane.add(imageLabel, JSplitPane.LEFT); 
      splitPane.add(buttonPanel, JSplitPane.RIGHT); 
      panel.add(splitPane); 
     } 
     JScrollPane spane = new JScrollPane(panel); 
     frame.add(spane); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setSize(500,600); 
     frame.setVisible(true); 
    } 
} 

如果要动态地加载图像,因为它们变得可见,你必须使用空JPanels为每个图像,而不是ImageIcons,然后你会覆盖JPanels的paint方法。只有JPanel可见时才会调用paint方法。所以最简单的解决方案是始终在绘制方法中从磁盘加载图像,然后将其绘制到屏幕上。

import javax.swing.*; 
import java.awt.*; 
import java.awt.image.*; 
import javax.imageio.ImageIO; 
import java.io.File; 

public class Scroll extends JPanel { 
    public static void main(String[] args) throws Exception { 
     JFrame frame = new JFrame(); 
     JPanel panel = new Scroll(); 

     panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 

     for(int i = 0; i < 10; i++) { 
      JPanel buttonPanel = new JPanel(); 
      JRadioButton b1 = new JRadioButton("button 1"); 
      JRadioButton b2 = new JRadioButton("button 2"); 
      JRadioButton b3 = new JRadioButton("button 3"); 
      ButtonGroup group = new ButtonGroup(); 
      group.add(b1); 
      group.add(b2); 
      group.add(b3); 
      buttonPanel.add(b1); 
      buttonPanel.add(b2); 
      buttonPanel.add(b3); 

      JPanel imagePanel = new JPanel() { 
       { 
        BufferedImage image = ImageIO.read(new File("image.jpg")); 
        setPreferredSize(new Dimension(image.getWidth(), image.getHeight())); 
        image.flush(); 
       } 
       @Override 
       public void paint(Graphics g) { 
        try { 
         BufferedImage image = ImageIO.read(new File("image.jpg")); 
         g.drawImage(image, 0, 0, null); 
         image.flush(); 
        } catch(Exception e) { 
        } 
       } 
      }; 

      JSplitPane splitPane = new JSplitPane(); 
      splitPane.add(imagePanel, JSplitPane.LEFT); 
      splitPane.add(buttonPanel, JSplitPane.RIGHT); 
      panel.add(splitPane); 
     } 
     JScrollPane spane = new JScrollPane(panel); 
     frame.add(spane); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setSize(500,600); 
     frame.setVisible(true); 
    } 
} 
+0

感谢您的代码片段。我正在尝试使用第二种方法,问题是,按照目前的结构,当按下'重新加载图像'按钮时,侦听器重新扫描它所在的目录并添加大量JLabels将图像作为ImageIcons。 JLabels是在一个单独的类中创建的。我假设我会重写该类中的paint()并使用它扩展JPanel,但paint()仍然没有被调用。哪里会是最适合重写它的地方? –

0

如果这是一个“使用一次就扔掉”类型的程序,我不打算试图解决这个问题。我会使用命令行为每个图片生成缩略图,并在我的程序中使用生成的缩略图。在内存中保留700张缩略图应该是可行的。

如果这不是一个选项,我会从JTable开始。您可以创建一个TableModel而不必将所有图像加载到内存中。你只需要知道图像数量。 JTable将只想渲染当时可见的图像。警告可能是在渲染器询问它们时加载图像可能太晚了。我可以想象,当你不得不加载大小为几MB的图片时,JTable将无法​​顺利运行。但是,这可能可以通过使用一个缓存来解决,该缓存使用工作线程来填充。因此,如果渲染器请求第5张图片,则还会将之前的5张图片和5张下一张图片加载到缓存中。

另请注意,如果您正在将图片复制到另一个图片,这可能会影响您的TableModel,因为您目录中的图片数量将发生变化。请务必考虑!

+0

我只是试图用这个程序学习一些东西,所以即使它是一次性使用的东西,我想尝试按照它的意思来构建它。感谢关于JTable的提示,我甚至没有想到这一点。 –