Ejemplo: agregar Autocompletar a JTextField

Autocompletar puede ser muy útil en casi cualquier aplicación, pero no es trivial de implementar. Así que aquí hay un ejemplo rápido de cómo podrías hacerlo en Java's Sw...

Autocompletar puede ser muy útil en casi cualquier aplicación, pero no es trivial de implementar. Así que aquí hay un ejemplo rápido de cómo puede hacerlo en el marco Swing de Java con JTextField (también debería funcionar con JTextArea con solo unas pocas modificaciones). Este ejemplo es una versión modificada de el ejemplo de oracle.

El Código

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package com.example.myapp.console;

import java.awt.event.ActionEvent;
import java.util.Collections;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;

public class Autocomplete implements DocumentListener {

  private static enum Mode {
    INSERT,
    COMPLETION
  };

  private JTextField textField;
  private final List<String> keywords;
  private Mode mode = Mode.INSERT;

  public Autocomplete(JTextField textField, List<String> keywords) {
    this.textField = textField;
    this.keywords = keywords;
    Collections.sort(keywords);
  }

  @Override
  public void changedUpdate(DocumentEvent ev) { }

  @Override
  public void removeUpdate(DocumentEvent ev) { }

  @Override
  public void insertUpdate(DocumentEvent ev) {
    if (ev.getLength() != 1)
      return;
 
    int pos = ev.getOffset();
    String content = null;
    try {
      content = textField.getText(0, pos + 1);
    } catch (BadLocationException e) {
      e.printStackTrace();
    }
 
    // Find where the word starts
    int w;
    for (w = pos; w >= 0; w--) {
      if (!Character.isLetter(content.charAt(w))) {
        break;
      }
    }

    // Too few chars
    if (pos - w < 2)
      return;
 
    String prefix = content.substring(w + 1).toLowerCase();
    int n = Collections.binarySearch(keywords, prefix);
    if (n < 0 && -n <= keywords.size()) {
      String match = keywords.get(-n - 1);
      if (match.startsWith(prefix)) {
        // A completion is found
        String completion = match.substring(pos - w);
        // We cannot modify Document from within notification,
        // so we submit a task that does the change later
        SwingUtilities.invokeLater(new CompletionTask(completion, pos + 1));
      }
    } else {
      // Nothing found
      mode = Mode.INSERT;
    }
  }

  public class CommitAction extends AbstractAction {
    /**
     * 
     */
    private static final long serialVersionUID = 5794543109646743416L;

    @Override
    public void actionPerformed(ActionEvent ev) {
      if (mode == Mode.COMPLETION) {
        int pos = textField.getSelectionEnd();
        StringBuffer sb = new StringBuffer(textField.getText());
        sb.insert(pos, " ");
        textField.setText(sb.toString());
        textField.setCaretPosition(pos + 1);
        mode = Mode.INSERT;
      } else {
        textField.replaceSelection("\t");
      }
    }
  }

  private class CompletionTask implements Runnable {
    private String completion;
    private int position;
 
    CompletionTask(String completion, int position) {
      this.completion = completion;
      this.position = position;
    }
 
    public void run() {
      StringBuffer sb = new StringBuffer(textField.getText());
      sb.insert(position, completion);
      textField.setText(sb.toString());
      textField.setCaretPosition(position + completion.length());
      textField.moveCaretPosition(position);
      mode = Mode.COMPLETION;
    }
  }

}

Usando el Código

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
private static final String COMMIT_ACTION = "commit";
JTextField mainTextField = new JTextField();

// Without this, cursor always leaves text field
mainTextField.setFocusTraversalKeysEnabled(false);
...
// Our words to complete
keywords = new ArrayList<String>(5);
        keywords.add("example");
        keywords.add("autocomplete");
        keywords.add("wikihtp");
        keywords.add("java");
Autocomplete autoComplete = new Autocomplete(mainTextField, keywords);
mainTextField.getDocument().addDocumentListener(autoComplete);

// Maps the tab key to the commit action, which finishes the autocomplete
// when given a suggestion
mainTextField.getInputMap().put(KeyStroke.getKeyStroke("TAB"), COMMIT_ACTION);
mainTextField.getActionMap().put(COMMIT_ACTION, autoComplete.new CommitAction());

Si desea que la lista de palabras clave cambie con el tiempo en su aplicación (por ejemplo, si está autocompletando nombres de amigos en línea en una aplicación de chat), entonces todo lo que necesita hacer es agregar un método de establecimiento a Autocompletar para List<String> keywords y actualice la lista de palabras clave con la lista de Strings que desee.