我需要实现一个“可搜索”
JComboBox
:在您键入时过滤其显示的项目
受到这个答案的启发,我写了这个实现
package di;
import org.apache.commons.lang3.StringUtils;
import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
public class ComboBoxes {
private ComboBoxes() {
}
public static <T> JComboBox<T> searchableComboBox() {
return searchableComboBox(new ArrayList<>());
}
public static <T> JComboBox<T> searchableComboBox(Collection<T> items) {
ComboBoxModel<T> model = new DefaultComboBoxModel<>(new Vector<>(items));
JComboBox<T> comboBox = new JComboBox<>(model);
comboBox.setEditable(true);
ComboBoxEditor comboBoxEditor = comboBox.getEditor();
JTextField editorTextField = (JTextField) comboBoxEditor.getEditorComponent();
KeyAdapter searchableComboBoxKeyListener = createSearchableComboBoxKeyListener(comboBox, model);
editorTextField.addKeyListener(searchableComboBoxKeyListener);
return comboBox;
}
private static <T> KeyAdapter createSearchableComboBoxKeyListener(JComboBox<T> comboBox, ComboBoxModel<T> initialModel) {
return new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
JTextField editorTextField = (JTextField) comboBox.getEditor().getEditorComponent();
String enteredText = editorTextField.getText();
ArrayList<T> matchingElements = findMatchingElements(enteredText);
DefaultComboBoxModel<T> modelWithOnlyMatchingElements = new DefaultComboBoxModel<>(new Vector<>(matchingElements));
comboBox.setModel(modelWithOnlyMatchingElements);
comboBox.setSelectedItem(enteredText);
comboBox.showPopup();
}
private ArrayList<T> findMatchingElements(String enteredText) {
ArrayList<T> matchingElements = new ArrayList<>();
for (int i = 0; i < initialModel.getSize(); i++) {
T modelElement = initialModel.getElementAt(i);
if (StringUtils.containsIgnoreCase(modelElement.toString(), enteredText))
matchingElements.add(modelElement);
}
return matchingElements;
}
};
}
}
它与弦乐配合得很好。它对于自定义对象有点没问题,但有一个小问题
按键监听器将输入的字符串设置为新的选定项。它可以摆脱这一点,因为
setSelectedItem()
接受 Object
,即使 JComboBox
是参数化类型(因为泛型的引入远远晚于 Swing,JComboBox
并不是 真正泛型)
因此,如果您收听新的选择并期望所选项目属于
JComboBox
的类型参数,您将得到 ClassCastException
,因为字符串无法转换为该类型。这是一个演示
package demos.combo;
import di.ComboBoxes;
import javax.swing.*;
import java.awt.event.ItemEvent;
import java.util.ArrayList;
public class SearchableComboBoxDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Searchable Combo Box Demo");
JPanel mainPanel = new JPanel();
JComboBox<Plant> searchableCombo = ComboBoxes.searchableComboBox();
searchableCombo.addItemListener(SearchableComboBoxDemo::onItemSelection);
comboBoxItems().forEach(searchableCombo::addItem);
mainPanel.add(searchableCombo);
frame.setContentPane(mainPanel);
frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static void onItemSelection(ItemEvent event) {
if (event.getStateChange() != ItemEvent.SELECTED) return;
Plant selectedItem = (Plant) event.getItem(); // throws
System.out.printf("Selected item: " + selectedItem + "\n");
}
private static ArrayList<Plant> comboBoxItems() {
ArrayList<Plant> items = new ArrayList<>();
items.add(new Plant("Potato"));
items.add(new Plant("Peach"));
items.add(new Plant("Banana"));
items.add(new Plant("Orange"));
items.add(new Plant("Carrot"));
items.add(new Plant("Cabbage"));
return items;
}
}
package demos.combo;
public class Plant {
private final String name;
public Plant(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
setSelectedItem()
电话很重要。如果将其删除,则在尝试删除字符后文本字段将恢复有效值。这是设定新模型的副作用
有人会认为直接设置文本字段的文本就可以解决问题
comboBox.setModel(modelWithOnlyMatchingElements);
editorTextField.setText(enteredText); // like this
comboBox.showPopup();
但是
JComboBox
不会按预期触发 ItemEvent
。尝试输入“o”,然后单击“橙色”,然后选择并删除所有文本,然后选择“土豆”。您不会收到“选择土豆”事件。这是因为当清除该字段并设置新模型时,新模型的选定项(本例中为第一项)会自动分配给 JComboBox
的 selectedItemReminder
字段
/*
the invoked DefaultComboBoxModel constructor automatically selects the first element,
there's nothing we can do about it
*/
public DefaultComboBoxModel(Vector<E> v) {
objects = v;
if ( getSize() > 0 ) {
selectedObject = getElementAt( 0 );
}
}
如果用户的选择与其匹配(确实如此),则不会触发
IventItem
(请参阅 javax.swing.JComboBox#setSelectedItem
)
所以,总而言之,我如何实现一个可搜索的
JComboBox
:
ItemListener
可以听?
一种解决方法是将
selectedItem
设置为 null
comboBox.setModel(modelWithOnlyMatchingElements);
comboBox.setSelectedItem(null);
editorTextField.setText(enteredText);
comboBox.showPopup();