Быстрая замена для JComboBox / BasicComboBoxUI? - PullRequest
2 голосов
/ 07 июля 2010

У меня есть JComboBox, который потенциально может иметь тысячи предметов.Они отсортированы, и есть поиск типа как вы, так что в принципе это не совсем непригодно для использования.

На практике это довольно непригодно только с парой сотен предметов.Мне удалось улучшить начальную производительность отображения с помощью setPrototypeDisplayValue(), но BasicListUI все еще настаивает на настройке средства визуализации ячеек списка для каждого элемента в поле (см. BasicListUI.updateLayoutState()).

Это или что-то подобноеявляется, по-видимому, известной проблемой для Sun;Прошло уже восемь лет, поэтому я не затаил дыхание.

Если не считать реализации собственного интерфейса, есть ли у кого-нибудь обходной путь?

Ответы [ 2 ]

2 голосов
/ 07 июля 2010

JList может быть лучшим выбором, так как в нем используется метод наименьшего веса для рендеринга и, как представляется, поддерживается поиск по мере ввода.

Если вы используете 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);
    }
}
0 голосов
/ 09 июля 2010

Вот хак, который я придумал.Недостатки:

  • Если вы хотите сохранить внешний вид и внешний вид, вы должны отдельно разделить на подклассы каждое расширение BasicComboBoxUI, о котором вы заботитесь
  • , вам нужно использовать отражение для загрузкиКлассы пользовательского интерфейса, поскольку (например) подкласс 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;
    }
  };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...