2

我有一个JComboBox可能有数千个项目的东西。它们是经过分类的,并且有“随你输入”的功能,因此原则上它并非完全不可用。

在实践中,只有几百个项目是非常不可用的。我设法使用 提高了初始显示性能setPrototypeDisplayValue(),但BasicListUI仍然坚持为框中的每个项目配置列表单元格渲染器(请参阅 参考资料BasicListUI.updateLayoutState())。

这或类似的东西,显然是Sun已知的问题。它已经持续了八年,所以我没有屏住呼吸。

没有实现我自己的 UI,有没有人有解决方法?

4

2 回答 2

2

JList可能是一个更好的选择,因为它使用轻量方法进行渲染,并且似乎支持 find-as-you-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);
    }
}
于 2010-07-07T16:53:48.353 回答
0

这是我想出的技巧。缺点是:

  • 如果你想保持外观和感觉,你必须单独对BasicComboBoxUI你关心的每个扩展进行子类化
  • 你必须使用反射来加载你的 UI 类,因为(例如)一个子类WindowsComboBoxUI不会在 Linux 上加载
  • 它不适用于不扩展的 L&F(例如 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;
    }
  };
}
于 2010-07-09T14:08:01.167 回答