代码之家  ›  专栏  ›  技术社区  ›  Elliott

显示格式为HTML的大型jlist

  •  1
  • Elliott  · 技术社区  · 16 年前

    我有一个Java小程序,我必须显示大量的条目(字典条目)。用户需要能够选择列表中的单个项目,因此它作为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 回复  |  直到 12 年前
        1
  •  1
  •   Michael Borgwardt    16 年前

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

        2
  •  1
  •   Community Mohan Dere    9 年前

    上面的链接有点过时,所以这里有一些更新的东西。

    简单地使用jtable可以极大地提高初始加载时的速度,但是当您第一次开始滚动时会有点慢。还有一个新的问题就是行高需要调整。这可以在自定义渲染器内部完成,通过实现 TableCellRenderer 自从 getTableCellRendererComponent 方法允许您访问行、表和组件。但是,这将触发将调用相同代码的表的更新。如果您进行适当的编码,这不会是问题。不过,最好还是把它放在别的地方。我添加了一个听众到 JViewport 并且只更新当前在视图中的行。 The code I based this on is here

    或者,您可以使用write a ListCellRenderer 返回一个 JPanel 看起来像HTML。如果你只需要一个 JTextArea 然后你需要设置它的宽度,以确保它的首选高度设置正确。 like in this answer . 同样,您必须更新行的宽度,并且根据 JVIEWPORT .

    如果您对这两种方法的性能都很好奇,那么定制的渲染器将返回 面板 比快 JLabel s呈现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();
                    }
                }
            });
    
        }
    }