【问题标题】:Changing cursor when rendering a JList渲染 JList 时更改光标
【发布时间】:2011-11-26 06:34:59
【问题描述】:

我已经实现了我想要做的事情,但我不禁想到有一种更有效的方法……请允许我举例说明。

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

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

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

下面是我的渲染器的代码,在其中你会看到我正在计算渲染器的迭代次数。如果该值介于 0 和 N-20 之间,则光标变为等待光标。一旦达到 N-20,它就会恢复为默认光标。就像我之前提到的,这工作得很好,但这个解决方案真的感觉像是一个 hack。我已经实现了 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));

【问题讨论】:

  • 添加了更多代码,但我不确定它是否有助于解决问题。我正在寻找的是一种在不实现计数器的情况下确定组件是否完成初始渲染的方法。
  • 永远不要(如:nononono,根本不要)改变渲染器中调用列表的状态。 getXXRendererComponent 中给出的所有参数都是严格只读的。正如@trashgod 已经建议的那样,将您的逻辑移到其他地方

标签: java swing render jlist mouse-cursor


【解决方案1】:

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

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

3) Renderer 可以 setBackground, Font 任何方法(在这种情况下)JLabel

编辑:

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

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

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

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

  • 已移除,请参见第 3 点。
  • 在 BackGroung 任务中准备,而不是在渲染器和加载 + Html 格式化 + 向后交互到渲染器之间构建构造函数,在这种情况下渲染器无用,因为您可以使用 Html 预先格式化项目,然后删除渲染器李>

【讨论】:

  • 谢谢,但我相信您误解了这个问题。光标正在父窗口上设置(通过全局变量 m_This)。就像我在帖子中所说,我的实现工作正常,我只是好奇是否有一种方法可以优雅地确定 JList 的初始渲染何时完成。
  • 我同意;尽可能尝试将渲染器的任何加载/格式化 outinto 工作线程。
  • @Josh:感谢您接受这个全面的答案。我也鼓励你投票。
【解决方案2】:

除了@mKorbel 建议,使用SwingWorker 批量填充列表模型。向工作人员添加一个属性更改侦听器,并在完成后恢复光标。有相关问答here

private static class TaskListener implements PropertyChangeListener {

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

【讨论】:

  • 对不起,错误的开始; SwingWorker 在事件调度线程上通知PropertyChangeListeners
  • 嘿,伙计们,只是想提个醒,我不会放弃这个...只是遇到了一个更紧迫的问题,我还没有机会仔细阅读您的建议。再次感谢所有帮助。
  • 谢谢大家,这正是我想要的,更重要的是我对发生的事情有了更好的了解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-21
  • 1970-01-01
  • 2019-01-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多