TITLE:JComboBoxで候補一覧を表示

Posted by at 2004-12-06

JComboBoxで候補一覧を表示

JComboBoxに入力候補の一覧表示機能(補完機能、コードアシスト、コンテンツアシスト)を追加します。

ComboBoxSuggestion.png

サンプルコード

String[] array = {
    "aaaa", "aaaabbb", "aaaabbbcc", "aaaabbbccddd",
    "abcde", "abefg", "bbb1", "bbb12"};
JComboBox combo = new JComboBox(array);
combo.setEditable(true);
combo.setSelectedIndex(-1);
JTextField field = (JTextField)combo.getEditor().getEditorComponent();
field.setText("");
field.addKeyListener(new ComboKeyHandler(combo));
View in GitHub: Java, Kotlin
class ComboKeyHandler extends KeyAdapter{
  private final JComboBox comboBox;
  private final Vector<String> list = new Vector<>();
  public ComboKeyHandler(JComboBox combo) {
    this.comboBox = combo;
    for(int i=0;i<comboBox.getModel().getSize();i++) {
      list.addElement((String)comboBox.getItemAt(i));
    }
  }
  private boolean shouldHide = false;
  @Override public void keyTyped(final KeyEvent e) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        String text = ((JTextField)e.getSource()).getText();
        if(text.length()==0) {
          setSuggestionModel(comboBox, new DefaultComboBoxModel(list), "");
          comboBox.hidePopup();
        }else{
          ComboBoxModel m = getSuggestedModel(list, text);
          if(m.getSize()==0 || shouldHide) {
            comboBox.hidePopup();
          }else{
            setSuggestionModel(comboBox, m, text);
            comboBox.showPopup();
          }
        }
      }
    });
  }
  @Override public void keyPressed(KeyEvent e) {
    JTextField textField = (JTextField)e.getSource();
    String text = textField.getText();
    shouldHide = false;
    switch(e.getKeyCode()) {
      case KeyEvent.VK_RIGHT:
        for(String s: list) {
          if(s.startsWith(text)) {
            textField.setText(s);
            return;
          }
        }
        break;
      case KeyEvent.VK_ENTER:
        if(!list.contains(text)) {
          list.addElement(text);
          Collections.sort(list);
          setSuggestionModel(comboBox, getSuggestedModel(list, text), text);
        }
        shouldHide = true;
        break;
      case KeyEvent.VK_ESCAPE:
        shouldHide = true;
        break;
    }
  }
  private static void setSuggestionModel(JComboBox comboBox, ComboBoxModel mdl, String str) {
    comboBox.setModel(mdl);
    comboBox.setSelectedIndex(-1);
    ((JTextField)comboBox.getEditor().getEditorComponent()).setText(str);
  }
  private static ComboBoxModel getSuggestedModel(Vector<String> list, String text) {
    DefaultComboBoxModel m = new DefaultComboBoxModel();
    for(String s: list) {
      if(s.startsWith(text)) m.addElement(s);
    }
    return m;
  }
}

解説

上記のサンプルでは、次のキー操作に対応しています。

  • UpDownキー
    • ポップアップ表示
  • Escキー
    • ポップアップ非表示
  • Rightキー
    • 補完
  • Enterキー
    • 選択、または追加
  • 文字入力
    • 候補をポップアップ

JComboBox#showPopup()JComboBox#hidePopup()(それぞれ、JComboBox#setPopupVisibleメソッドをラップしているだけ)を使って、候補のポップアップメニュー表示を制御します。

JComboBox#setSelectedIndex(-1)で、項目の選択をクリアしないと動作がおかしくなる場合があります。


JComboBoxではなく、SwingSet3JHistoryTextField.java のように、JTextField+JPopupMenuを使用することもできますが、画面の下側で候補数が変更された場合のJPopupMenuの位置更新(気にしなければ問題無し)が面倒です。JTextField風に見せかけたいだけなら、以下のようなArrowButtonを非表示にする方法もあります。

//UIManager.put("ComboBox.squareButton", Boolean.FALSE);
JComboBox = new JComboBox(model) {
  @Override public void updateUI() {
    super.updateUI();
    setUI(new javax.swing.plaf.basic.BasicComboBoxUI() {
      @Override protected JButton createArrowButton() {
        JButton button = new JButton() {
          @Override public int getWidth() {
            return 0;
          }
        };
        button.setBorder(BorderFactory.createEmptyBorder());
        button.setVisible(false);
        return button;
      }
      @Override public void configureArrowButton() {}
    });
    for(MouseListener ml:getMouseListeners()) {
      removeMouseListener(ml);
    }
  }
};

コメント

  • 変換途中の日本語も、問題がないともっといいですね。 -- toshi
    • あー、日本語のこと全然考えてなかったです…。 -- aterai
  • タイトルなどを変更するとしたらAutoCompletionに? -- aterai
  • 日本語を考えるとKeyReleasedよりKeyTypedのほうがよさそうです -- foggi
    • ご指摘ありがとうございます。keyTypedに変更してみました(ついでにスクリーンショットなども更新)。 -- aterai
  • Enterキーでの追加が出来なくなっていたのを修正しました。 -- aterai
  • 下下と入力したとき、下上と入力したときの動きがおかしいです。 -- magi
    • 自分の環境では再現できてません。カーソルキーを、「下、下、下、上」と入力するのでしょうか? -- aterai