Ответ 1
Технический ответ: установить для свойства popup focusable значение false:
popup.setFocusable(false);
Импликация заключается в том, что textField должен взять на себя все действия с клавиатурой и мышью, которые обычно обрабатываются самим списком, sosmething like:
final JList list = new JList(Locale.getAvailableLocales());
final JPopupMenu popup = new JPopupMenu();
popup.add(new JScrollPane(list));
popup.setFocusable(false);
final JTextField field = new JTextField(20);
Action down = new AbstractAction("nextElement") {
@Override
public void actionPerformed(ActionEvent e) {
int next = Math.min(list.getSelectedIndex() + 1,
list.getModel().getSize() - 1);
list.setSelectedIndex(next);
list.ensureIndexIsVisible(next);
}
};
field.getActionMap().put("nextElement", down);
field.getInputMap().put(
KeyStroke.getKeyStroke("DOWN"), "nextElement");
Поскольку ваш контекст очень похож на JComboBox, вы можете подумать о том, чтобы изучить источники BasicComboBoxUI и BasicComboPopup.
Edit
Просто для удовольствия, следующее не отвечает на заданный вопрос:-) Вместо этого он демонстрирует, как использовать сортируемый/фильтруемый JXList, чтобы показывать только параметры в раскрывающемся списке, соответствующие типизированному тексту (здесь, с правилом)
// instantiate a sortable JXList
final JXList list = new JXList(Locale.getAvailableLocales(), true);
list.setSortOrder(SortOrder.ASCENDING);
final JPopupMenu popup = new JPopupMenu();
popup.add(new JScrollPane(list));
popup.setFocusable(false);
final JTextField field = new JTextField(20);
// instantiate a PatternModel to map text --> pattern
final PatternModel model = new PatternModel();
model.setMatchRule(PatternModel.MATCH_RULE_STARTSWITH);
// listener which to update the list RowFilter on changes to the model pattern property
PropertyChangeListener modelListener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("pattern".equals(evt.getPropertyName())) {
updateFilter((Pattern) evt.getNewValue());
}
}
private void updateFilter(Pattern newValue) {
RowFilter<Object, Integer> filter = null;
if (newValue != null) {
filter = RowFilters.regexFilter(newValue);
}
list.setRowFilter(filter);
}
};
model.addPropertyChangeListener(modelListener);
// DocumentListener to update the model rawtext property on changes to the field
DocumentListener documentListener = new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
updateAfterDocumentChange();
}
@Override
public void insertUpdate(DocumentEvent e) {
updateAfterDocumentChange();
}
private void updateAfterDocumentChange() {
if (!popup.isVisible()) {
popup.show(field, 0, field.getHeight());
}
model.setRawText(field.getText());
}
@Override
public void changedUpdate(DocumentEvent e) {
}
};
field.getDocument().addDocumentListener(documentListener);