2011-09-27 94 views
3

我已經實現了我正在嘗試做的事情,但我不禁想到有一種更高效的方式......請允許我說明。渲染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>"; 
    } 
} 

我做的填充模式是這樣的:

// 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)); 
+0

你能否提供更多的代碼? – StanislavL

+0

添加了更多代碼,但我不確定它是否有助於解決問題。我正在尋找的是一種確定組件是否已完成初始渲染而不實施計數器的方法。 – JoshBramlett

+0

從來沒有(如:不不不不不,乾脆不要)在渲染改變調用列表的狀態。 getXXRendererComponent中給出的所有參數都是嚴格只讀的。別的地方移動你的邏輯@trashgod已經建議 – kleopatra

回答

3

1)你必須添加CursorJList,內部沒有Renderer,例如herehere

2) Renderer默認返回JLabel,沒有什麼特別的理由來定義那個

3)Renderer可以setBackgroundFont無論對於方法(在這種情況下)的JLabel

編輯:

4)除去retVal.setText(formatListBoxOutput((String)value, index));創建DefaulListModel並移動準備的物品到型號#的addItem

5)int index,可以比較JList#getModel().getSize() -1;刪除從Renderer,

6)更好的是將添加您的項目從BackGround任務(@trashgod你的建議是正確的:-)和填充如果你要實現SwingWorker,那麼你可以創建批量爲50的項目並添加50 ...直到完成

7)渲染器僅用於格式化輸出,你的Html格式化可以是:

  • 刪除,休息見第3點。
  • 在BackGroung Task中準備,而不是在Renderer和加載+ Html之間構建構造函數+向渲染器的後向交互,在這種情況下渲染器無用,因爲您可以使用Html預處理項目,然後刪除渲染器
+0

謝謝你,但我相信你誤解的問題。遊標正在父窗口上設置(通過全局變量m_This)。就像我在帖子中所說的,我的實現工作,我只是簡單地好奇,是否有一種方法來優雅地確定JList的初始渲染何時完成。 – JoshBramlett

+0

請看到我的編輯,@trashgod感謝踢 – mKorbel

+0

我同意;儘可能多地嘗試移動渲染器的任何加載/格式化工作線程和_into_工作線程。 – trashgod

4

除了@mKorbel建議,使用SwingWorker填寫批次列表模型。向工作人員添加一個屬性更改偵聽器,並在完成時恢復光標。有一個相關的Q & A here

private static class TaskListener implements PropertyChangeListener { 

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

對不起, 'SwingWorker'在事件派發線程上通知'PropertyChangeListeners'。 – trashgod

+0

嘿,夥計,只是想擡起頭來,我不會放棄這個......只是有一個更緊迫的問題,我沒有機會通過你的建議。再次感謝所有的幫助。 – JoshBramlett

+0

謝謝你們,這正是我所期待的,更重要的是我對發生的事情有了更好的理解。 – JoshBramlett