快速替换JComboBox / BasicComboBoxUI?

我有一个可能有数千个项目的JComboBox 。 它们是有序的,并且有找到你的类型,所以原则上它并非完全无法使用。

在实践中,只有几百个项目它几乎无法使用。 我设法使用setPrototypeDisplayValue()来改善初始显示性能,但BasicListUI仍然坚持为框中的每个项配置列表单元格渲染器(请参阅BasicListUI.updateLayoutState() )。

这或类似的东西显然是Sun的一个已知问题 ; 现在已经八年了,所以我没有屏住呼吸。

没有实现我自己的用户界面,有没有人有一个解决方法?

JList可能是更好的选择,因为它使用fly-weight方法进行渲染,并且似乎支持find-as-type-type。

如果使用JComboBox ,请在组件本身开始侦听之前向模型添加条目。 这个SortedComboBoxModel使用一个简单的插入排序 ,可以接受几千个条目:

 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); } } 

这是我提出的黑客行为。 缺点是:

  • 如果你想保持外观,你必须单独子类化你关心的每个BasicComboBoxUI扩展
  • 你必须使用reflection来加载你的UI类,因为(例如) WindowsComboBoxUI的子类将不会在Linux上加载
  • 它不适用于不扩展BasicComboBoxUI L&F(例如MacOS?)
  • 它对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); } } } } 

我仍然愿意接受更清洁的解决方案。

后来

原来这只解决了一些问题。 具有大量项目的combobox的初始显示可能仍然非常慢。 我必须通过将代码移动到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; } }; }