快速更换JComboBox / BasicComboBoxUI?

时间:2010-07-07 13:12:41

标签: java swing jcombobox

我有JComboBox可能有数千件物品。它们是有序的,并且有找到你的类型,所以原则上它并不完全无法使用。

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

这个,或类似的东西,显然是太阳的known issue;现在已经八年了,所以我不会屏住呼吸。

如果没有实施我自己的用户界面,有没有人有解决方法?

2 个答案:

答案 0 :(得分:2)

JList可能是更好的选择,因为它使用了飞行重量方法进行渲染,并且似乎支持“按类型查找”。

如果您使用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);
    }
}

答案 1 :(得分:0)

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

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