Пример: добавление автозаполнения в JTextField


Author:  Mich

Автозаполнение может быть очень полезно практически в любом приложении, но его нетривиально реализовать. Итак, вот краткий пример того, как вы могли бы сделать это в Java-фреймворке Swing с JTextField (он также должен работать с JTextArea с небольшими изменениями). Этот пример представляет собой модифицированную версию примера Oracle .

Код

 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; 
 } 
 } 
 
 } 

Использование кода

 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("stackabuse"); 
 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()); 

Если вы хотите, чтобы список ключевых слов в вашем приложении со временем менялся (например, если вы автоматически заполняете имена друзей онлайн в приложении чата), то все, что вам нужно сделать, это добавить метод установки в Autocomplete for List<String> keywords и обновите список ключевых слов нужным Strings


Смотрите также