2010-07-07 111 views
2

我有一個JComboBox可能可以有數千個項目。它們被排序,並且有即髮型,所以原則上它不是完全無法使用的。快速替換JComboBox/BasicComboBoxUI?

實際上,只有幾百件物品是無法使用的。我設法使用setPrototypeDisplayValue()提高了初始顯示性能,但BasicListUI仍然堅持爲該框中的每個項目配置列表單元格渲染器(請參閱BasicListUI.updateLayoutState())。

這個或類似的東西顯然是Sun的known issue;現在已經過去八年了,所以我並沒有屏住呼吸。

缺乏實施我自己的用戶界面,有沒有人有解決方法?

+6

在我的小見解中,除了表現之外,我會擔心用戶體驗。如果組合框中可能有數千個項目,那麼也許是時候嘗試將它們分類(並有第二個組合框),或讓另一個組件找到您的值。你有一個「找到你鍵入」的方法,也許你應該有一個專門的組件,從不顯示整個列表,只有一個過濾視圖。它也將解決性能問題。 – Gnoupi 2010-07-07 13:22:27

+1

@mcandre,如果我想要我的散文風格的建議,我不會來堆棧溢出。相同的JComboBox可能有一個項目或零。一個典型的數字是十。 「可能會有」不是多餘的,它是準確的。 – 2010-07-08 07:16:22

回答

0

下面是我想出了破解。缺點是:

  • ,如果你想保持外觀和感覺,你必須單獨繼承每個BasicComboBoxUI擴展你照顧,你必須使用反射來加載UI類,距今約
  • (例如)的WindowsComboBoxUI子類不會在Linux上加載
  • 它不會與L- &工作FS(例如MacOS的?)不擴展BasicComboBoxUI
  • 它使有關ListCellRenderer假設可能並不總是保證

我仍然接受更清潔的解決方案。

class FastBasicComboBoxUI extends BasicComboBoxUI { 
    @Override 
    public void installUI(JComponent c) { 
    super.installUI(c); 

    Object prototypeValue = this.comboBox.getPrototypeDisplayValue(); 
    if (prototypeValue != null) { 
     ListCellRenderer renderer = comboBox.getRenderer(); 
     Component rendererComponent = renderer 
      .getListCellRendererComponent(this.listBox, 
       prototypeValue, 0, false, false); 
     if (rendererComponent instanceof JLabel) { 
     // Preferred size of the renderer itself is (-1,-1) at this point, 
     // so we need this hack 
     Dimension prototypeSize = new JLabel(((JLabel) rendererComponent) 
      .getText()).getPreferredSize(); 
     this.listBox.setFixedCellHeight(prototypeSize.height); 
     this.listBox.setFixedCellWidth(prototypeSize.width); 
     } 
    } 
    } 
} 

我仍然接受更清潔的解決方案。

後來

事實證明這只是解決了一些問題。包含大量項目的組合框的初始顯示仍然很慢。我必須確保彈出列表框立即通過將代碼移動到ComboPopup本身獲得固定的單元大小,如下所示。請注意,如上所述,這取決於原型值。

@Override 
protected ComboPopup createPopup() { 
    return new BasicComboPopup(comboBox) { 
    @Override 
    protected JList createList() { 
     JList list = super.createList(); 
     Object prototypeValue = comboBox.getPrototypeDisplayValue(); 
     if (prototypeValue != null) { 
     ListCellRenderer renderer = comboBox.getRenderer(); 
     Component rendererComponent = renderer 
      .getListCellRendererComponent(list, prototypeValue, 0, false, false); 
     if (rendererComponent instanceof JLabel) { 
      // Preferred size of the renderer itself is (-1,-1) at this point, 
      // so we need this hack 
      Dimension prototypeSize = new JLabel(((JLabel) rendererComponent) 
       .getText()).getPreferredSize(); 
      list.setFixedCellHeight(prototypeSize.height); 
      list.setFixedCellWidth(prototypeSize.width); 
     } 
     } 
     return list; 
    } 
    }; 
} 
2

JList可能是一個更好的選擇,因爲它使用fly-weight方法來呈現,並且似乎支持按照您的類型查找。

如果您使用JComboBox,則在之前將組件添加到模型,組件本身開始偵聽。這SortedComboBoxModel使用一個簡單的insertion sort這是幾千項可以接受的:

class SortedComboBoxModel extends DefaultComboBoxModel { 

    /** Add elements by inserting in lexical order. */ 
    @Override 
    public void addElement(Object element) { 
     this.insertElementAt(element, 0); 
    } 

    /** Insert in lexical order by name; ignore index. */ 
    @Override 
    public void insertElementAt(Object element, int index) { 
     String name = element.toString(); 
     for (index = 0; index < this.getSize(); index++) { 
      String s = getElementAt(index).toString(); 
      if (s.compareTo(name) > 0) { 
       break; 
      } 
     } 
     super.insertElementAt(element, index); 
    } 
} 
+0

這不是真正的問題 - 該模型在組合框存在之前完全構建,並且對其進行更改是全有或全無。 – 2010-07-09 13:58:09