显示格式为HTML的大型JLIST

时间:2010-06-22 09:24:43

标签: java html jlist

我有一个java applet,我必须在其中显示大量项目(字典条目)。用户需要能够在列表中选择单个项目,因此它被实现为JList。生成列表非常快,直到我决定通过使用HTML格式化单个项目来使显示更令人愉悦。现在列表看起来很漂亮,但每次用户访问字典时生成它都需要10到15秒(没有格式化它几乎立即发生)。我想我可以通过在用户第一次进入应用程序时生成列表并根据需要隐藏和取消隐藏列表来预测性能。但是,我想知道是否有更好的方法。也许是一种生成列表的更有效方法。

以下是发生减速的代码部分(在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时,我获得了真正的性能。我能做些什么来解决这个问题?

谢谢,

2 个答案:

答案 0 :(得分:1)

您使用的是哪种HTML格式?如果只是一些文字样式(字体,颜色),您可以使用JLabel,相应地设置其属性,JList设置set it as ListCellRenderer

答案 1 :(得分:1)

上面的链接有点过时,所以这里有更新的内容。

简单地使用JTable是初始加载速度的一个巨大改进,但是当你第一次开始滚动时有点慢。而且你有行高需要调整的新问题。这可以通过实现TableCellRenderer在自定义渲染器内部轻松完成,因为getTableCellRendererComponent方法可以访问行,表和组件。然而,这将触发将调用相同代码的表的更新。如果你编码适当,这不会是一个问题。不过,将它放在其他地方更好。我向JViewport添加了一个监听器,并且只更新了当前正在查看的行。 The code I based this on is here

或者,您可以使用写ListCellRenderer来返回看起来像HTML的JPanel。如果您只需要JTextArea,那么您需要设置其宽度以确保正确设置首选高度like in this answer。同样,您必须更新行的宽度,并且根据JViewport执行此操作是有意义的。

如果您对这两种方法的性能感到好奇,那么返回JPanel的自定义渲染器比呈现HTML的JLabel要快。尽管列表中有几千个项目,但两者都相当快。如上所述,当你最初滚动时,它们会有点慢。

最后,这里有一些代码可以让你自己进行快速比较:

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

    }
}