这是我想出的技巧。缺点是:
- 如果你想保持外观和感觉,你必须单独对
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;
}
};
}