我有一个JTextField,我希望建议结果匹配用户的输入.我在JPopupMenu中包含的JList中显示这些建议.
但是,当通过show(Component invoker,int x,int y)
以编程方式打开弹出菜单时,焦点将从JTextField中获取.
奇怪的是,如果我调用setVisible(true)代替,焦点不会被盗;但是然后JPopupMenu不附加到任何面板,并且当框打开时最小化应用程序时,它将保持画在窗口上.
我也尝试使用requestFocus()将焦点重置到JTextField,但是我必须使用SwingUtilities.invokeLater()还原插入符号位置,并且调用后面的东西给用户一个微小的余地与现有内容/覆盖/或做其他不可预测的事情.
我的代码是有效的:
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,field.getHeight()); } });
任何人都可以建议最好的途径,以编程方式显示JPopupMenu,同时保留对JTextField的关注?
解决方法
技术答案是将弹出的对焦属性设置为false:
popup.setFocusable(false);
这意味着textField必须接管通常由列表本身处理的所有键盘和鼠标触发的操作,sosmething类似于:
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,field.getHeight()); } model.setRawText(field.getText()); } @Override public void changedUpdate(DocumentEvent e) { } }; field.getDocument().addDocumentListener(documentListener);