渲染JList时更改光标

时间:2011-09-27 17:51:23

标签: java swing render jlist mouse-cursor

我已经实现了我想要做的事情,但我不禁想到有一种更有效的方式......让我来说明一下。

简而言之,我问的问题是,是否有办法确定组件何时完成初始渲染。

我有一个JList,它连接到一个DefaultListModel,并由一个扩展DefaultListCellRenderer的自定义渲染器绘制。

JList的这个意图是通过日志文件“页面”,每次加载新页面时填充2500个元素。在我的机器上,完全呈现JList通常需要几秒钟,这实际上不是问题,因为将光标更改为等待光标是可以接受的,因为它会给用户提供即时反馈。不幸的是,我无法找到一种优雅的方式来了解初始渲染何时完成。

下面是我的渲染器的代码,在其中你会看到我正在计算渲染器上的迭代次数。如果它在0和N-20之间,则光标变为等待光标。一旦达到N-20,它将恢复为默认光标。就像我之前提到的,这很好用,但解决方案真的像一个黑客。我已经实现了DefaultListModel的ListDataListener和JList的PropertyChangeListener,但是都没有生成我正在寻找的功能。

/**
 * Renders the MessageHistory List based on the search text and processed events
 */
public class MessageListRenderer extends DefaultListCellRenderer
{
    private static final long serialVersionUID = 1L;

    String lineCountWidth = Integer.toString(Integer.toString(m_MaxLineCount).length());

    @Override
    public Component getListCellRendererComponent(JList l, Object value, int index, boolean isSelected, boolean haveFocus) 
    {
        JLabel retVal = (JLabel)super.getListCellRendererComponent(l, value, index, isSelected, haveFocus);
        retVal.setText(formatListBoxOutput((String)value, index));

        // initial rendering is beginning - change the cursor
        if ((renderCounter == 0) && !renderCursorIsWait)
        {
            m_This.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));   
            renderCursorIsWait = true;
        }

        // initial rendering is complete - change the cursor back
        if ((renderCounter > (l.getModel().getSize() - 20)) && renderCursorIsWait)
        {
            m_This.setCursor(Cursor.getDefaultCursor());
            renderCursorIsWait = false;
        }

        renderCounter++;

        return retVal;
    }

    /**
     * Adds font tags (marks as red) around all values which match the search criteria
     * 
     * @param lineValue string to search in 
     * @param lineIndex line number being processed
     * @return string containing the markup
     */
    private String formatListBoxOutput(String lineValue, int lineIndex)
    {
        // Theoretically the count should never be zero or less, but this will avoid an exception just in case
        if (m_MaxLineCount <= 0)
            return lineValue;

        String formattedLineNumber = String.format("%" + Integer.toString(Integer.toString(m_MaxLineCount).length()) + "s", m_CurrentPage.getStartLineNumber() + lineIndex) + ": ";

        // We're not searching, simply return the line plus the added line number
        if((m_lastSearchText == null) || m_lastSearchText.isEmpty())
            return "<html><font color=\"#4682B4\">" + formattedLineNumber.replaceAll(" ", "&nbsp;") + "</font>" + lineValue + "</html>";

        // break up the search string by the search value in case there are multiple entries 
        String outText = "";    
        String[] listValues = lineValue.split(m_lastSearchText);

        if(listValues.length > 1)
        {
            // HTML gets rid of the preceding whitespace, so change it to the HTML code
            outText = "<html><font color=\"#4682B4\">" + formattedLineNumber.replaceAll(" ", "&nbsp;") + "</font>";

            for(int i = 0; i < listValues.length; i++)
            {
                outText += listValues[i];
                if(i + 1 < listValues.length)
                    outText += "<font color=\"red\">" + m_lastSearchText + "</font>";
            }

            return outText + "</html>";
        }

        return "<html><font color=\"#4682B4\">" + formattedLineNumber.replaceAll(" ", "&nbsp;") + "</font>" + lineValue + "</html>";
    }
}

我正在填充模型的所有内容是:

// reset the rendering counter
this.renderCounter = 0;

// Reset the message history and add all new values
this.modelLogFileData.clear();
for (int i = 0; i < this.m_CurrentPage.getLines().size(); i++)
    this.modelLogFileData.addElement(this.m_CurrentPage.getLines().elementAt(i));

2 个答案:

答案 0 :(得分:4)

除了@mKorbel建议外,还可以使用SwingWorker批量填充列表模型。将属性更改侦听器添加到worker,并在完成后还原光标。有一个相关的Q&amp; A here

private static class TaskListener implements PropertyChangeListener {

    @Override
    public void propertyChange(PropertyChangeEvent e) {
        if (e.getNewValue() == SwingWorker.StateValue.DONE) {
            // restore cursor
        }
    }
}

答案 1 :(得分:3)

1)您已将Cursor添加到JList,而不是Renderer,示例herehere

2)Renderer默认返回JLabel,没有特殊原因来定义

3)Renderer可以setBackgroundFont JLabel

的任何方法(在这种情况下)

编辑:

4)删除retVal.setText(formatListBoxOutput((String)value, index));创建DefaulListModel并将准备好的项目移动到Model#addItem

5)int index,可以与JList#getModel().getSize() -1;进行比较,从渲染器中删除

6)更好的是从BackGround任务添加你的项目(@trashgod你的建议是正确的:-)并填写批量(谢谢),如果你将实现SwingWorker,那么你可以在50个项目创建批处理并添加到50 ......直到完成

7)渲染器仅用于格式化输出,您的Html格式化可能是:

  • 删除,休息见第3点。
  • 在BackGroung Task中编写,而不是在Renderer和loading + Html Formating + Backward交互到Renderer之间构建构造函数,在这种情况下渲染器没用,因为你可以通过使用Html预先格式化项目,然后删除渲染器