Java Swing可搜索JComboBox异常:无搜索结果时编辑框内容不恢复
可搜索JComboBox的文本恢复异常问题
需求与问题
功能需求
- 点击JComboBox时展开选项列表,支持输入进行多词搜索
- 匹配到结果时可点击选中;无匹配项时列表为空
- 点击组件外部时,禁用输入、隐藏列表,未选中项则恢复最近选中项或默认值
异常现象
搜索有匹配项时,点击外部能正常恢复到最近选中项;但无匹配项时点击外部,编辑框内容无法恢复。疑问:editor.setText(selectedMenuItem.toString());为何仅在有搜索结果时生效?
相关代码
CustomClass[] items = customClassList.toArray(new CustomClass[customClassList.size()]); comboBox = new JComboBox<CustomClass>(items); selectedMenuItem = comboBox.getItemAt(0); currentCustomClassID = selectedMenuItem.getCustomClassID(); Dimension comboBoxSize = comboBox.getPreferredSize(); comboBoxSize.height = 25; comboBoxSize.width = 500; comboBox.setMinimumSize(comboBoxSize); comboBox.setMaximumSize(comboBoxSize); comboBox.setPreferredSize(comboBoxSize); comboBox.addActionListener(e -> { Object selected = comboBox.getSelectedItem(); if (selected instanceof CustomClass) { selectedMenuItem = (CustomClass) selected; comboBox.setEditable(false); currentCustomClassID = selectedMenuItem.getCustomClassID(); } }); JTextField editor = (JTextField) comboBox.getEditor().getEditorComponent(); menuPane.add(comboBox); comboBox.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { comboBox.setEditable(true); } }); comboBox.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { // Popup is opening } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { } // This is where the text inside the box should change when the user clicks outside the combo box. It changes when there are options available, but not if the options list is empty @Override public void popupMenuCanceled(PopupMenuEvent e) { comboBox.setEditable(false); editor.setText(selectedMenuItem.toString()); comboBox.setSelectedItem(selectedMenuItem); } }); editor.addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { comboBox.setEditable(true); editor.setText(""); comboBox.showPopup(); } }); editor.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { String text = editor.getText(); String[] keywords = text.toLowerCase().split(" "); // Filter items that contain all keywords Vector<CustomClass> filteredItems = new Vector<>(); for (CustomClass item : items) { boolean match = true; for (String keyword : keywords) { if (!item.toString().toLowerCase().contains(keyword)) { match = false; break; } } if (match) filteredItems.add(item); } // Update Model DefaultComboBoxModel<CustomClass> newModel = new DefaultComboBoxModel<>(filteredItems); comboBox.setModel(newModel); comboBox.setSelectedItem(text); comboBox.showPopup(); } });
问题原因分析
- 无效选中项的干扰:当搜索无匹配项时,你执行了
comboBox.setSelectedItem(text),而这个text是用户输入的字符串,并不在当前空的ComboBox模型中。此时ComboBox处于可编辑状态,编辑器文本与选中项绑定。 - 代码顺序导致覆盖:在
popupMenuCanceled方法中,你先调用comboBox.setEditable(false),这时候Swing会自动同步编辑器文本为当前选中的无效字符串,之后执行的editor.setText(...)会被组件的内部更新逻辑覆盖。 - 有匹配项时的正常逻辑:当搜索有结果时,
comboBox.setSelectedItem(text)会匹配到模型中的CustomClass对象,选中项有效,后续设置编辑器文本不会被覆盖。
解决方案
调整恢复逻辑的代码顺序
修改popupMenuCanceled方法,先恢复选中项和编辑器文本,再设置不可编辑,避免Swing的自动同步覆盖你的设置:
@Override public void popupMenuCanceled(PopupMenuEvent e) { comboBox.setSelectedItem(selectedMenuItem); editor.setText(selectedMenuItem.toString()); comboBox.setEditable(false); }
优化搜索时的选中逻辑
在keyReleased方法中,当过滤结果为空时,不要设置选中项为用户输入的文本,避免产生无效选中项:
// Update Model DefaultComboBoxModel<CustomClass> newModel = new DefaultComboBoxModel<>(filteredItems); comboBox.setModel(newModel); // 仅当有过滤结果时,选中第一个匹配项 if (!filteredItems.isEmpty()) { comboBox.setSelectedItem(filteredItems.get(0)); } comboBox.showPopup();
内容的提问来源于stack exchange,提问作者Ian McInnes
相关产品推荐
相关产品推荐

