15 txtInput.setText(cbInput.getSelectedItem().toString());
16 cbInput.setPopupVisible(false);
17 }
18 }
19 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
20 cbInput.setPopupVisible(false);
21 }
22 setAdjusting(cbInput, false);
23 }
24 });
还有一个非常重要的技术要点要进行说明。在popup列表弹出的时候,我们希望用箭头能够上下移动选择条目,但是又同时希望当前的光标和焦点不要离开文本框。这个好像非常难实现啊!请看我们是如何做到的:在监控到上下箭头输入时候,把当前的键盘事件的source动态修改为JComboBox,然后派发给JComboBox。也就是说,本来事件是输入到文本框的,我们把邮递员拦截下来,把收件人改一下,继续交给邮递员进行派发。这样,就做到“移花接木”了:
1 if (e.getKeyCode() == KeyEvent.VK_ENTER || e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN) {
2 e.setSource(cbInput);
3 cbInput.dispatchEvent(e);
4 if (e.getKeyCode() == KeyEvent.VK_ENTER) {
5 txtInput.setText(cbInput.getSelectedItem().toString());
6 cbInput.setPopupVisible(false);
7 }
8 }
最后,为了演示效果,我们放一些数据到下拉列表中。放什么呢?自己造假数据太麻烦了,干脆用Java中的“所有国家”的数据吧,简单省事:
1 Locale[] locales = Locale.getAvailableLocales();
2 for (int i = 0; i < locales.length; i++) {
3 String item = locales[i].getDisplayName();
4 items.add(item);
5 }
最后看一下效果,完全符合我们的预期:
以下是完整代码:
1 import java.awt.*;
2 import java.awt.event.*;
3 import java.util.*;
4
5 import javax.swing.*;
6 import javax.swing.event.*;
7
8 import twaver.*;
9
10 public class Test {
11
12 public static void main(String[] args) throws Exception {
13 UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
14 JFrame frame = new JFrame();
15 frame.setTitle("Auto Completion Test");
16 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
17 frame.setBounds(200, 200, 500, 400);
18
19 ArrayList
20 Locale[] locales = Locale.getAvailableLocales();
21 for (int i = 0; i < locales.length; i++) {
22 String item = locales[i].getDisplayName(); www.2cto.com
23 items.add(item);
24 }
25 JTextField txtInput = new JTextField();
26 setupAutoComplete(txtInput, items);
27 txtInput.setColumns(30);
28 frame.getContentPane().setLayout(new FlowLayout());
29 frame.getContentPane().add(txtInput, BorderLayout.NORTH);
30 frame.setVisible(true);
31 }
32
33 private static boolean isAdjusting(JComboBox cbInput) {
34 if (cbInput.getClientProperty("is_adjusting") instanceof Boolean) {
35 return (Boolean) cbInput.getClientProperty("is_adjusting");
36 }
37 return false;
38 }
39
40 private static void setAdjusting(JComboBox cbInput, boolean adjusting) {
41 cbInput.putClientProperty("is_adjusting", adjusting);
42 }
43
44 public static void setupAutoComplete(final JTextField txtInput, final ArrayList
45 final DefaultComboBoxModel model = new DefaultComboBoxModel();
46 final JComboBox cbInput = new JComboBox(model) {
47 public Dimension getPreferredSize() {
48 return new Dimension(super.getPreferredSize().width, 0);
49 }
50 };
51 setAdjusting(cbInput, false);
52 for (String item : items) {
53 model.addElement(item);
54 }
55 cbI