2010-06-22 6 views
1

J'ai une applet Java dans laquelle je dois afficher une grande quantité d'éléments (entrées de dictionnaire). L'utilisateur doit être en mesure de sélectionner des éléments individuels dans la liste, d'où il est implémenté en tant que JList. Générer la liste a été très rapide jusqu'à ce que je décide de rendre l'affichage plus esthétiquement agréable en formatant les éléments individuels en utilisant le HTML. Maintenant, la liste est jolie mais il faut entre 10 et 15 secondes pour la générer chaque fois que l'utilisateur accède au dictionnaire (sans le formater, il se produit presque instantanément). Je suppose que je pourrais prendre le coup de la performance en générant la liste lorsque l'utilisateur entre pour la première fois dans l'application et juste cacher et afficher la liste au besoin. Mais, je me demande s'il y a un meilleur moyen. Peut-être un moyen plus efficace de générer la liste.Affichage de grandes JLIST au format HTML

Voici la section du code où les occurrs lents vers le bas (entre l'affichage de C et 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"); 

Comme vous pouvez le voir, il est assez simple. Quand "theToken" est formaté en HTML, j'obtiens un réel coup de performance. Des idées de ce que je peux faire pour contourner cela?

Merci,

Répondre

1

Quel type de formatage HTML utilisez-vous? Si c'est juste un style de texte (police, couleur), vous pouvez utiliser un JLabel, définir ses propriétés en conséquence et set it as ListCellRenderer pour le JList.

+0

Je vois où vous voulez en venir, mais je besoin de quelque chose qui va me permettre d'afficher une entrée multiple ligne. Voici une entrée formatée typique:

E995.Insertion de nouvelles lignes \ n ou retour de caractères \ r ne semble pas fonctionner avec JList même avec un ListCellRenderer. Suggestions? – Elliott

+0

@Elliott: vous pouvez utiliser un composant personnalisé (ou même simplement un LabelUI personnalisé: http://codeguru.earthweb.com/java/articles/198.shtml) pour rendre un texte multiligne tout en évitant l'analyse HTML (je profilerais d'abord pour confirmer si c'est effectivement le goulot d'étranglement). Alternativement, vous pouvez essayer d'utiliser une JTable à une seule colonne au lieu d'une JList; IIRC JTable est conçu pour de grandes quantités de données beaucoup plus que JList et ne restitue que les éléments réellement visibles. –

+0

J'ai terminé en utilisant un composant JTextArea pour afficher le texte. Il a fait l'affaire. La suggestion ListCellRenderer m'a cependant orienté dans la bonne direction. J'ai utilisé l'exemple de code suivant pour m'aider à passer ceci: http://forums.sun.com/thread.jspa?threadID=552711 – Elliott

1

Les liens ci-dessus sont un peu obsolètes donc voici quelque chose de plus à jour. Le simple fait d'utiliser JTable est une amélioration considérable de la vitesse de chargement initiale, mais un peu lent lorsque vous commencez à défiler. Et vous avez le nouveau problème que la hauteur de ligne doit ajuster. Cela peut être fait à l'intérieur d'un moteur de rendu personnalisé assez facilement en mettant en œuvre TableCellRenderer puisque la méthode getTableCellRendererComponent vous donne accès à la ligne, la table et le composant. Cela entraînera cependant une mise à jour de la table qui appellera le même code. Si vous codez correctement, ce ne sera pas un problème. Pourtant, il est préférable de le mettre ailleurs. J'ai ajouté un écouteur au JViewport et seulement mis à jour les lignes qui sont actuellement en vue. Vous pouvez également écrire un ListCellRenderer qui renvoie un JPanel qui ressemble à du code HTML. Si vous avez seulement besoin d'un JTextArea, vous devez définir sa largeur pour vous assurer que sa hauteur préférée est correctement définie like in this answer. Encore une fois, vous devez mettre à jour la largeur de la ligne et il est logique de le faire en fonction du JViewport.

Si vous êtes intéressé par les performances des deux approches, un rendu personnalisé renvoyant un JPanel est plus rapide que JLabel s rendu HTML. Les deux sont raisonnablement rapides mais même avec des listes avec quelques milliers d'articles. Comme mentionné, ils peuvent être un peu lent lorsque vous faites défiler initialement.

Enfin, voici un code qui vous permet de faire une comparaison rapide vous:

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

    } 
} 
Questions connexes