Java 渲染JList时更改光标

Java 渲染JList时更改光标,java,swing,render,jlist,mouse-cursor,Java,Swing,Render,Jlist,Mouse Cursor,我已经实现了我想要做的,但我不得不认为有一种更有效的方法…请允许我举例说明 简而言之,我要问的问题是,是否有办法确定组件何时完成其初始渲染 我有一个JList,它连接到DefaultListModel,并由扩展DefaultListCellRenderer的自定义渲染器绘制 JList的目的是通过日志文件“分页”,每次加载新页面时填充2500个元素。在我的机器上,完全渲染JList通常需要几秒钟的时间,这并不是一个真正的问题,因为将光标更改为等待光标是可以接受的,因为它会立即向用户提供反馈。不幸

我已经实现了我想要做的,但我不得不认为有一种更有效的方法…请允许我举例说明

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

我有一个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>";
    }
}
/**
*根据搜索文本和已处理事件呈现MessageHistory列表
*/
公共类MessageListRenderer扩展了DefaultListCellRenderer
{
私有静态最终长serialVersionUID=1L;
字符串lineCountWidth=Integer.toString(Integer.toString(m_MaxLineCount).length());
@凌驾
公共组件getListCellRenderComponent(JList l、对象值、int索引、布尔isSelected、布尔haveFocus)
{
JLabel retVal=(JLabel)super.getListCellRenderComponent(l,value,index,isSelected,haveFocus);
setText(formatListBoxOutput((字符串)值,索引));
//开始初始渲染-更改光标
如果((renderCounter==0)和&!renderCounter等待)
{
m_This.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_Cursor));
renderCursorIsWait=true;
}
//初始渲染完成-将光标更改回原位
if((renderCounter>(l.getModel().getSize()-20))&&renderCounter等待)
{
m_This.setCursor(Cursor.getDefaultCursor());
renderCursorIsWait=false;
}
renderCounter++;
返回返回;
}
/**
*在与搜索条件匹配的所有值周围添加字体标记(标记为红色)
* 
*@param lineValue要搜索的字符串
*@param lineIndex行号正在处理中
*@包含标记的返回字符串
*/
私有字符串formatListBoxOutput(字符串lineValue、int lineIndex)
{
//理论上,计数永远不应为零或更少,但这将避免一个异常,以防万一
如果(m_MaxLineCount1)您已将
光标添加到
JList
,而不是在
渲染器中,示例或

2) 默认情况下,
Renderer
返回
JLabel
,没有特别的理由定义它

3)
Renderer
可以
setBackground
Font
使用
JLabel

编辑:

4) 删除
retVal.setText(formatListBoxOutput((字符串)值,索引));
创建DefaulListModel并将准备好的项移动到模型#addItem

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

6) 最好是从后台任务中添加项目(@trashgood您的建议是正确的:-)并填写批次(谢谢),如果您执行SwingWorker,那么您可以创建50个项目的批次,并添加50个…直到完成

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

  • 拆除,其余部分见第3点
  • 在后台任务中准备,而不是在渲染器和加载+Html格式化+向后交互到渲染器之间构建构造函数,在这种情况下,渲染器是无用的,因为您可以使用Html对项目进行预格式化,然后删除渲染器

除了@mKorbel建议之外,还可以使用a批量填充列表模型。向worker添加属性更改侦听器,并在完成后还原光标。还有相关的问答


谢谢,但我相信你误解了这个问题。光标被设置在父窗口上(通过全局变量m_This)。正如我在文章中所说,我的实现是有效的,我只是想知道是否有一种方法可以优雅地确定JList的初始渲染何时完成。我同意;尽可能尝试将任何加载/格式化从渲染器移到工作线程中。@Josh:很高兴接受这个全面的答案。我希望请您也投票。添加了更多代码,但我不确定这是否有助于解决这个问题。我要寻找的是一种方法,以确定组件是否在未实现计数器的情况下完成其初始渲染。永远不要(如:nonono,干脆不要)更改渲染器中调用列表的状态。GetXXRenderComponent中给出的所有参数都是严格只读的。将您的逻辑移到其他地方,因为@trashgod已经建议了抱歉错误启动;
SwingWorker
在事件调度线程上通知
PropertyChangeListeners
。嘿,伙计们,我只是想给他一个提示我不会放弃这个…只是遇到了一个更紧急的问题,我还没有机会仔细考虑你的建议。再次感谢所有的帮助。谢谢你们,这正是我想要的,更重要的是,我对发生的事情有了更好的了解。
// 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));
private static class TaskListener implements PropertyChangeListener {

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