2010-06-22 45 views
1

我有一個java applet,我必須在其中顯示大量項目(字典條目)。用戶需要能夠選擇列表中的單個項目,因此它被實現爲JList。生成列表非常快,直到我決定通過使用HTML格式化單個項目來使顯示更美觀。現在這個列表看起來很漂亮,但每次用戶訪問字典時都需要10到15秒的時間來生成它(不需要格式化它幾乎立即發生)。我想我可以通過在用戶第一次進入應用程序時生成列表,並根據需要隱藏和取消隱藏列表來提高性能。但是,我想知道是否有更好的方法。也許更有效的方式來生成列表。顯示格式爲HTML的大型JLIST

這裏是代碼的部分,在那裏緩慢向下occurrs(C和d的顯示之間的關係):

DefaultListModel dictCodeModel = new DefaultListModel(); 
System.out.println("C"); 
while (e.hasMoreElements()) { 
    String currentEntry = ""; 
    DEntry dentry = (DEntry) e.nextElement(); 
    if (!filter)     
     dictCodeModel.addElement(dentry.theToken); // tokens have embedded html tags 

} 
System.out.println("D"); 

正如你可以看到它是相當簡單的。當「theToken」被格式化爲HTML時,我會得到真正的性能。任何想法,我可以做些什麼來解決這個問題?

謝謝,

回答

1

你使用什麼樣的HTML格式?如果只是一些文本樣式(字體,顏色),則可以使用JLabel,相應地設置其屬性,JList則使用set it as ListCellRenderer

+0

我看到你在哪裏與此打算,但我需要的東西,讓我來顯示多行條目。這是一個典型的格式化條目:

E995。9由於戰爭行動而受到其他未規定形式的常規戰爭造成的損害
未規定形式的常規戰爭
插入換行符\ n或返回字符\ r即使使用ListCellRenderer也無法使用JList。建議? – Elliott 2010-06-22 10:00:40

+0

@Elliott:你可以使用一個自定義組件(或者甚至只是一個自定義的LabelUI:http://codeguru.earthweb.com/java/articles/198.shtml)來呈現多行文本,同時避免HTML解析(我第一個配置文件以確認這是否實際上是瓶頸)。或者,您可以嘗試使用單列JTable而不是JList; IIRC JTable專爲大量數據而設計,遠遠超過JList,並且僅呈現實際可見的元素。 – 2010-06-22 11:06:07

+0

我使用JTextArea組件來清理文本。它做了詭計。 ListCellRenderer的建議雖然指出了我的正確方向。我使用下面的示例代碼來幫助我解決這個問題: http://forums.sun.com/thread.jspa?threadID=552711 – Elliott 2010-06-22 13:22:56

1

上面的鏈接有點過時,所以這裏有一些更新的東西。

簡單地使用JTable是初始加載速度的巨大改進,但是當您第一次開始滾動時會有點慢。而且你有行高需要調整的新問題。由於getTableCellRendererComponent方法允許您訪問行,表和組件,因此可以通過實施TableCellRenderer來輕鬆完成自定義渲染器內部的操作。然而,這會觸發表格的更新,它將調用相同的代碼。如果你編碼適當,這不會是一個問題。不過,最好將它放在別的地方。我向JViewport添加了一個偵聽器,並僅更新了當前正在查看的行。 The code I based this on is here

或者,您可以使用寫一個ListCellRenderer返回一個看起來像HTML的JPanel。如果你只需要一個JTextArea那麼你需要設置其寬度,以確保它的首選高度設置正確like in this answer。同樣,您必須更新行的寬度,根據JViewport這樣做是有道理的。

如果您對這兩種方法的性能感到好奇,那麼返回JPanel的自定義渲染器將比渲染HTML的速度更快。儘管列表中有幾千項,但兩者都相當快。如上所述,當您最初滾動時,它們可能會有點慢。

最後,這裏的一些代碼,可以讓用戶快速比較自己:

import java.awt.*; 
import java.awt.event.*; 
import java.util.*; 
import java.util.Timer; 
import java.util.concurrent.ExecutionException; 

import javax.swing.*; 
import javax.swing.event.*; 
import javax.swing.table.*; 

public class JTableHtmlTest extends JFrame { 

    protected static final long serialVersionUID = 1L; 

    public static class Item { 
     public int id; 
     public String msg; 
    } 

    static class TableModel extends AbstractTableModel { 

     private static final long serialVersionUID = JListTest.serialVersionUID; 
     private Item[] items = new Item[] {}; 

     public int getRowCount() { 
      return items.length; 
     } 

     public int getColumnCount() { 
      return 1; 
     } 

     public Object getValueAt(int rowIndex, int columnIndex) { 
      return items[rowIndex]; 
     } 

     @Override 
     public String getColumnName(int column) { 
      return ""; 
     } 

     public void updateItems() { 
      SwingWorker<Item[], Void> worker = new SwingWorker<Item[], Void>() { 

       @Override 
       protected Item[] doInBackground() throws Exception { 
        final Item[] tempList = new Item[3000]; 
        for (int i = 0; i < tempList.length; i++) { 
         Item item = new Item(); 
         item.id = (int) (Math.random() * 10000); 
         item.msg = "This is the default message that has to be" 
           + " long enough to wrap around a few times so that" 
           + " we know things are working. It's rather tedious to write."; 
         tempList[i] = item; 
        } 
        return tempList; 
       } 

       @Override 
       protected void done() { 
        try { 
         items = get(); 
         fireTableDataChanged(); 
        } catch (InterruptedException e) { 
         e.printStackTrace(); 
        } catch (ExecutionException e) { 
         e.printStackTrace(); 
        } 

       } 
      }; 
      worker.execute(); 
     } 

    } 

    public static class TableRenderer implements TableCellRenderer { 

     private static final String strColor = "#EDF5F4"; 
     private static final Color strideColor = Color.decode(strColor); 

     JLabel htmlLabel = new JLabel(); 
     JPanel noHtmlPanel = new JPanel(); 
     JLabel noHtmlLabel = new JLabel(); 
     JTextArea noHTMLTextArea = new JTextArea(); 
     Item toRender = null; 
     boolean useHtml = false; 

     public TableRenderer() { 
      noHTMLTextArea.setWrapStyleWord(false); 
      noHTMLTextArea.setLineWrap(true); 
      noHTMLTextArea.setOpaque(false); 

      Font defaultFont = noHtmlLabel.getFont(); 
      Font boldFont = defaultFont.deriveFont(Font.BOLD); 
      noHtmlLabel.setFont(boldFont); 
      noHtmlLabel.setOpaque(false); 

      noHtmlPanel.setLayout(new BorderLayout()); 
      noHtmlPanel.add(noHtmlLabel, BorderLayout.NORTH); 
      noHtmlPanel.add(noHTMLTextArea, BorderLayout.SOUTH); 
     } 

     public void setUseHtml(boolean useHtml) { 
      this.useHtml = useHtml; 
     } 

     public Component getJlabelRenderer(JTable table, Item value, int row) { 

      String colorString = ""; 
      if (row % 2 == 0) { 
       colorString = "background-color:" + strColor + ";"; 
      } 
      if (toRender != value) { 
       toRender = value; 
       htmlLabel.setText("<html><div style='padding:2px;" + "width:" 
         + table.getWidth() + ";" + colorString 
         + "color:black;'>" 
         + "<div style='padding:2px;font-weight:500;'>" 
         + "Item " + value.id + "</div>" + value.msg 
         + "</div></html>"); 
      } 

      return htmlLabel; 
     } 

     public Component getNoHtmlRenderer(JTable table, Item value, int row) { 
      if (toRender != value) { 
       toRender = value; 
       noHtmlLabel.setText("Item " + value.id); 
       noHTMLTextArea.setText(value.msg); 

       if (row % 2 == 0) { 
        noHtmlPanel.setBackground(strideColor); 
        noHtmlPanel.setOpaque(true); 
       } else { 
        noHtmlPanel.setOpaque(false); 
       } 
      } 

      return noHtmlPanel; 
     } 

     public Component getTableCellRendererComponent(JTable table, 
       Object value, boolean isSelected, boolean hasFocus, int row, 
       int column) { 
      if (useHtml) { 
       return getJlabelRenderer(table, (Item) value, row); 
      } else { 
       return getNoHtmlRenderer(table, (Item) value, row); 
      } 
     } 

    } 

    public JTableHtmlTest() { 
     this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     JPanel controlPanel = new JPanel(); 
     JButton updaterControl = new JButton("Update 3000"); 
     final JCheckBox useHtmlControl = new JCheckBox("Use HTML"); 
     final TableModel model = new TableModel(); 
     final JTable table = new JTable(model); 
     final TableRenderer renderer = new TableRenderer(); 
     JScrollPane scrollPane = new JScrollPane(table); 
     final JLabel durationIndicator = new JLabel("0"); 

     controlPanel.add(useHtmlControl, BorderLayout.WEST); 
     controlPanel.add(updaterControl, BorderLayout.EAST); 

     getContentPane().add(controlPanel, BorderLayout.PAGE_START); 
     getContentPane().add(scrollPane, BorderLayout.CENTER); 
     getContentPane().add(durationIndicator, BorderLayout.PAGE_END); 

     table.setDefaultRenderer(Object.class, renderer); 

     // Only update the JTable row heights when they are in view 
     final JViewport viewport = scrollPane.getViewport(); 
     viewport.addChangeListener(new ChangeListener() { 
      public void stateChanged(ChangeEvent e) { 
       Rectangle viewRect = viewport.getViewRect(); 
       int first = table.rowAtPoint(new Point(0, viewRect.y)); 
       if (first == -1) { 
        return; 
       } 
       int last = table.rowAtPoint(new Point(0, viewRect.y 
         + viewRect.height - 1)); 
       if (last == -1) { 
        last = model.getRowCount() - 1; 
       } 

       int column = 0; 
       for (int row = first; row <= last; row++) { 
        Component comp = table.prepareRenderer(
           table.getCellRenderer(row, column), 
           row, column); 
        int rowHeight = comp.getPreferredSize().height; 
        table.setRowHeight(row, rowHeight); 
       } 
      } 
     }); 

     updaterControl.addActionListener(new ActionListener() { 
      public void actionPerformed(ActionEvent e) { 
       renderer.setUseHtml(useHtmlControl.isSelected()); 
       model.updateItems(); 
      } 
     }); 

     Timer counter = new Timer(); 
     counter.schedule(new TimerTask() { 
      @Override 
      public void run() { 
       String previousCounter = durationIndicator.getText(); 
       final String newCounter = Integer.toString(Integer 
         .parseInt(previousCounter) + 1); 
       SwingUtilities.invokeLater(new Runnable() { 
        public void run() { 
         durationIndicator.setText(newCounter); 
         setTitle(newCounter); 
        } 
       }); 
      } 
     }, 0, 100); 
    } 

    public static void main(String args[]) { 
     EventQueue.invokeLater(new Runnable() { 
      public void run() { 
       try { 
        JTableHtmlTest jlt = new JTableHtmlTest(); 
        jlt.pack(); 
        jlt.setSize(300, 300); 
        jlt.setVisible(true); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 

    } 
}