2012-11-24 54 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的提示,我甚至沒有想到這一點。 –