我有一个JTextField
我希望建议结果与用户的输入相匹配。我在JList
中包含的JPopupMenu
中显示这些建议。
但,当通过show(Component invoker, int x, int y)
以编程方式打开弹出式菜单时,焦点将来自JTextField
。
奇怪的是,如果我打电话给setVisible(true)
,焦点就不会被盗;但是JPopupMenu
没有附加到任何面板上,当盒子打开时最小化应用程序时,它会一直涂在窗口上。
我还尝试使用JTextField
将焦点重置为requestFocus()
,但之后我必须使用SwingUtilities.invokeLater()
恢复插入位置,稍后调用< / em>事情的一面是给用户一点点余地来搞乱现有内容/覆盖它/或做其他不可预测的事情。
我得到的代码是有效的:
JTextField field = new JTextField();
JPopupMenu menu = new JPopupMenu();
field.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent e) {
JList list = getAListOfResults();
menu.add(list);
menu.show(field, 0, field.getHeight());
}
});
有人可以建议最好的途径,以编程方式显示JPopupMenu
,同时保留对JTextField
的关注吗?
答案 0 :(得分:14)
技术答案是将弹出窗口的focusable属性设置为false:
popup.setFocusable(false);
这意味着textField必须接管通常由列表本身处理的所有键盘和鼠标触发的操作,类似于:
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的来源。
修改强>
为了好玩,下面的不是回答焦点问题:-)而是演示了如何使用可排序/可过滤的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's RowFilter on changes to the model's 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's 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);
答案 1 :(得分:2)
它看起来很直接。添加以下
field.requestFocus();
之后
menu.add(list);
menu.show(field, 0, field.getHeight());
当然,您必须根据JTextField
的内容编码何时隐藏弹出窗口等。
即;
menu.show(field, field.getX(), field.getY()+field.getHeight());
menu.setVisible(true);
field.requestFocus();
答案 2 :(得分:1)
您可以查看JXSearchField,它是xswingx
的一部分