Crear un cuadro de texto autocompletado en Java con una lista desplegable

Quiero crear un cuadro de texto que sugiere automáticamente la base de datos en cada evento de lanzamiento de tecla. Esa parte es fácil, pero quiero darle una buena visual. Algo similar al cuadro de texto de sugestión automática que vemos en sitios web como buscar en Facebook.

¿Cómo hacer una interfaz así?

Una idea ingenua sería colocar una JList justo debajo del cuadro de texto y establecerlo visible con los resultados para encontrar uno.

¿Alguna idea mejor o una forma estándar de hacerlo?

La respuesta de @ syb0rg es más fácil, ya que utiliza una biblioteca de terceros.

Sin embargo, utilicé un enfoque alternativo:

Utiliza una clase personalizada llamada AutoSuggestor que acepta un JTextField , su Window una ArrayList de palabras para verificar las palabras escritas contra, un color de fondo y color de texto, y color de foco de sugerencia, así como un valor de opacidad. Al pasar la referencia de JTextField se agrega un DocumentListener que hará el trabajo de verificar qué palabra se escribe y si mostrar sugerencias o no y, en caso afirmativo, qué sugerencias mostrar. Cuando se escribe una palabra, DocumentListener activará el wordTyped(String wordTyped) con la palabra actual que se escribe o (al menos, cuántas palabras se han tipeado), en wordTyped(..) la palabra se wordTyped(..) con las de el diccionario de clases de AutoSuggestor , que es una ArrayList de String básica, se puede configurar sobre la marcha como se ve en el siguiente ejemplo:

enter image description here

(Por ahora tendrá que usar el mouse y hacer clic en la palabra que desea que se complete automáticamente, o usar ABAJO para hacer sugerencias transversales y el campo de texto y ENTER para seleccionar la sugerencia al atravesar usando la tecla Abajo . Todavía no he implementado UP ):

 import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.JWindow; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.border.LineBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; /** * @author David */ public class Test { public Test() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JTextField f = new JTextField(10); AutoSuggestor autoSuggestor = new AutoSuggestor(f, frame, null, Color.WHITE.brighter(), Color.BLUE, Color.RED, 0.75f) { @Override boolean wordTyped(String typedWord) { //create list for dictionary this in your case might be done via calling a method which queries db and returns results as arraylist ArrayList words = new ArrayList<>(); words.add("hello"); words.add("heritage"); words.add("happiness"); words.add("goodbye"); words.add("cruel"); words.add("car"); words.add("war"); words.add("will"); words.add("world"); words.add("wall"); setDictionary(words); //addToDictionary("bye");//adds a single word return super.wordTyped(typedWord);//now call super to check for any matches against newest dictionary } }; JPanel p = new JPanel(); p.add(f); frame.add(p); frame.pack(); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { new Test(); } }); } } class AutoSuggestor { private final JTextField textField; private final Window container; private JPanel suggestionsPanel; private JWindow autoSuggestionPopUpWindow; private String typedWord; private final ArrayList dictionary = new ArrayList<>(); private int currentIndexOfSpace, tW, tH; private DocumentListener documentListener = new DocumentListener() { @Override public void insertUpdate(DocumentEvent de) { checkForAndShowSuggestions(); } @Override public void removeUpdate(DocumentEvent de) { checkForAndShowSuggestions(); } @Override public void changedUpdate(DocumentEvent de) { checkForAndShowSuggestions(); } }; private final Color suggestionsTextColor; private final Color suggestionFocusedColor; public AutoSuggestor(JTextField textField, Window mainWindow, ArrayList words, Color popUpBackground, Color textColor, Color suggestionFocusedColor, float opacity) { this.textField = textField; this.suggestionsTextColor = textColor; this.container = mainWindow; this.suggestionFocusedColor = suggestionFocusedColor; this.textField.getDocument().addDocumentListener(documentListener); setDictionary(words); typedWord = ""; currentIndexOfSpace = 0; tW = 0; tH = 0; autoSuggestionPopUpWindow = new JWindow(mainWindow); autoSuggestionPopUpWindow.setOpacity(opacity); suggestionsPanel = new JPanel(); suggestionsPanel.setLayout(new GridLayout(0, 1)); suggestionsPanel.setBackground(popUpBackground); addKeyBindingToRequestFocusInPopUpWindow(); } private void addKeyBindingToRequestFocusInPopUpWindow() { textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released"); textField.getActionMap().put("Down released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) {//focuses the first label on popwindow for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) { if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) { ((SuggestionLabel) suggestionsPanel.getComponent(i)).setFocused(true); autoSuggestionPopUpWindow.toFront(); autoSuggestionPopUpWindow.requestFocusInWindow(); suggestionsPanel.requestFocusInWindow(); suggestionsPanel.getComponent(i).requestFocusInWindow(); break; } } } }); suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released"); suggestionsPanel.getActionMap().put("Down released", new AbstractAction() { int lastFocusableIndex = 0; @Override public void actionPerformed(ActionEvent ae) {//allows scrolling of labels in pop window (I know very hacky for now :)) ArrayList sls = getAddedSuggestionLabels(); int max = sls.size(); if (max > 1) {//more than 1 suggestion for (int i = 0; i < max; i++) { SuggestionLabel sl = sls.get(i); if (sl.isFocused()) { if (lastFocusableIndex == max - 1) { lastFocusableIndex = 0; sl.setFocused(false); autoSuggestionPopUpWindow.setVisible(false); setFocusToTextField(); checkForAndShowSuggestions();//fire method as if document listener change occured and fired it } else { sl.setFocused(false); lastFocusableIndex = i; } } else if (lastFocusableIndex <= i) { if (i < max) { sl.setFocused(true); autoSuggestionPopUpWindow.toFront(); autoSuggestionPopUpWindow.requestFocusInWindow(); suggestionsPanel.requestFocusInWindow(); suggestionsPanel.getComponent(i).requestFocusInWindow(); lastFocusableIndex = i; break; } } } } else {//only a single suggestion was given autoSuggestionPopUpWindow.setVisible(false); setFocusToTextField(); checkForAndShowSuggestions();//fire method as if document listener change occured and fired it } } }); } private void setFocusToTextField() { container.toFront(); container.requestFocusInWindow(); textField.requestFocusInWindow(); } public ArrayList getAddedSuggestionLabels() { ArrayList sls = new ArrayList<>(); for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) { if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) { SuggestionLabel sl = (SuggestionLabel) suggestionsPanel.getComponent(i); sls.add(sl); } } return sls; } private void checkForAndShowSuggestions() { typedWord = getCurrentlyTypedWord(); suggestionsPanel.removeAll();//remove previos words/jlabels that were added //used to calcualte size of JWindow as new Jlabels are added tW = 0; tH = 0; boolean added = wordTyped(typedWord); if (!added) { if (autoSuggestionPopUpWindow.isVisible()) { autoSuggestionPopUpWindow.setVisible(false); } } else { showPopUpWindow(); setFocusToTextField(); } } protected void addWordToSuggestions(String word) { SuggestionLabel suggestionLabel = new SuggestionLabel(word, suggestionFocusedColor, suggestionsTextColor, this); calculatePopUpWindowSize(suggestionLabel); suggestionsPanel.add(suggestionLabel); } public String getCurrentlyTypedWord() {//get newest word after last white spaceif any or the first word if no white spaces String text = textField.getText(); String wordBeingTyped = ""; if (text.contains(" ")) { int tmp = text.lastIndexOf(" "); if (tmp >= currentIndexOfSpace) { currentIndexOfSpace = tmp; wordBeingTyped = text.substring(text.lastIndexOf(" ")); } } else { wordBeingTyped = text; } return wordBeingTyped.trim(); } private void calculatePopUpWindowSize(JLabel label) { //so we can size the JWindow correctly if (tW < label.getPreferredSize().width) { tW = label.getPreferredSize().width; } tH += label.getPreferredSize().height; } private void showPopUpWindow() { autoSuggestionPopUpWindow.getContentPane().add(suggestionsPanel); autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textField.getWidth(), 30)); autoSuggestionPopUpWindow.setSize(tW, tH); autoSuggestionPopUpWindow.setVisible(true); int windowX = 0; int windowY = 0; windowX = container.getX() + textField.getX() + 5; if (suggestionsPanel.getHeight() > autoSuggestionPopUpWindow.getMinimumSize().height) { windowY = container.getY() + textField.getY() + textField.getHeight() + autoSuggestionPopUpWindow.getMinimumSize().height; } else { windowY = container.getY() + textField.getY() + textField.getHeight() + autoSuggestionPopUpWindow.getHeight(); } autoSuggestionPopUpWindow.setLocation(windowX, windowY); autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textField.getWidth(), 30)); autoSuggestionPopUpWindow.revalidate(); autoSuggestionPopUpWindow.repaint(); } public void setDictionary(ArrayList words) { dictionary.clear(); if (words == null) { return;//so we can call constructor with null value for dictionary without exception thrown } for (String word : words) { dictionary.add(word); } } public JWindow getAutoSuggestionPopUpWindow() { return autoSuggestionPopUpWindow; } public Window getContainer() { return container; } public JTextField getTextField() { return textField; } public void addToDictionary(String word) { dictionary.add(word); } boolean wordTyped(String typedWord) { if (typedWord.isEmpty()) { return false; } //System.out.println("Typed word: " + typedWord); boolean suggestionAdded = false; for (String word : dictionary) {//get words in the dictionary which we added boolean fullymatches = true; for (int i = 0; i < typedWord.length(); i++) {//each string in the word if (!typedWord.toLowerCase().startsWith(String.valueOf(word.toLowerCase().charAt(i)), i)) {//check for match fullymatches = false; break; } } if (fullymatches) { addWordToSuggestions(word); suggestionAdded = true; } } return suggestionAdded; } } class SuggestionLabel extends JLabel { private boolean focused = false; private final JWindow autoSuggestionsPopUpWindow; private final JTextField textField; private final AutoSuggestor autoSuggestor; private Color suggestionsTextColor, suggestionBorderColor; public SuggestionLabel(String string, final Color borderColor, Color suggestionsTextColor, AutoSuggestor autoSuggestor) { super(string); this.suggestionsTextColor = suggestionsTextColor; this.autoSuggestor = autoSuggestor; this.textField = autoSuggestor.getTextField(); this.suggestionBorderColor = borderColor; this.autoSuggestionsPopUpWindow = autoSuggestor.getAutoSuggestionPopUpWindow(); initComponent(); } private void initComponent() { setFocusable(true); setForeground(suggestionsTextColor); addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent me) { super.mouseClicked(me); replaceWithSuggestedText(); autoSuggestionsPopUpWindow.setVisible(false); } }); getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), "Enter released"); getActionMap().put("Enter released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) { replaceWithSuggestedText(); autoSuggestionsPopUpWindow.setVisible(false); } }); } public void setFocused(boolean focused) { if (focused) { setBorder(new LineBorder(suggestionBorderColor)); } else { setBorder(null); } repaint(); this.focused = focused; } public boolean isFocused() { return focused; } private void replaceWithSuggestedText() { String suggestedWord = getText(); String text = textField.getText(); String typedWord = autoSuggestor.getCurrentlyTypedWord(); String t = text.substring(0, text.lastIndexOf(typedWord)); String tmp = t + text.substring(text.lastIndexOf(typedWord)).replace(typedWord, suggestedWord); textField.setText(tmp + " "); } } 

Tal como está, las únicas adiciones posibles que se necesitan son:

  • Tecla ARRIBA Traversablity de enfoque para elementos dentro del cuadro emergente de autosugerencias para que podamos ir hacia arriba.

Si hay algún problema, déjame saber lo que puedo hacer. Pero parece estar funcionando bien (tocar madera).

Una forma realmente fácil de hacerlo es utilizar la implementación GlazedList de autocompletar. Es muy fácil comenzar a funcionar. Puedes encontrarlo aquí .

Puede instalar el autocompletado en un JComboBox con solo una línea de código Glazed, como esta:

 JComboBox comboBox = new JComboBox(); Object[] elements = new Object[] {"Cat", "Dog", "Lion", "Mouse"}; AutoCompleteSupport.install(comboBox, GlazedLists.eventListOf(elements)); 

También SwingX admite el autocompletado y podría ser más fácil de usar que GlazedList . Todo lo que escribes con SwingX es AutoCompleteDecorator.decorate(comboBox);

Para utilizar la clase TextAutoCompleter necesita descargar un archivo jar AutoCompleter.jar y agregarlo a la carpeta de la biblioteca de su proyecto y aquí está el enlace para descargar: http://download1689.mediafire.com/4grrthscpsug/7pwzgefiomu392o/AutoCompleter.jar -Nawin

// En la clase principal escribe el siguiente código

 package autocomplete; import com.mxrck.autocompleter.TextAutoCompleter; import java.sql.SQLException; import javax.swing.JFrame; import javax.swing.JTextField; public class AutoComplete { JFrame f=new JFrame(); JTextField t1; AutoComplete() throws ClassNotFoundException, SQLException{ f.setSize(500,500); f.setLocation(500,100); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setLayout(null); f.setVisible(true); t1=new JTextField(); t1.setBounds(50,80,200,20); f.add(t1); TextAutoCompleter complete=new TextAutoCompleter(t1); DBConn conn=new DBConn(); conn.connection(); conn.retrieve(); while(conn.rs.next()){ complete.addItem(conn.rs.getString("number")); } } public static void main(String[] args) throws ClassNotFoundException, SQLException{ new AutoComplete(); } } //Create seperate class for database connection and write the following code package autocomplete; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBConn { Connection con; ResultSet rs;PreparedStatement stat; public void connection() throws ClassNotFoundException, SQLException{ String url="jdbc:mysql://localhost:3306/"; String driver="com.mysql.jdbc.Driver"; String db="demo"; String username="root"; String password="root"; stat =null; Class.forName(driver); con=(Connection)DriverManager.getConnection (url+db,username,password); System.out.println("Connecttion SuccessFul"); } public void retrieve() throws SQLException{ Statement stmt=con.createStatement(); String query="select number from phone"; rs = stmt.executeQuery(query); System.out.println("retrieve succesfully"); } 

}

Quería completar automáticamente el editor en mi ensamblador AVR IDE, así que escribí una implementación que funciona igual que la autocompletación en Eclipse (activación de CTRL-SPACE, lista desplegable con barras de desplazamiento, teclas de cursor + navegación del mouse). No tiene dependencias externas y es solo una clase. Debería funcionar para todas las subclases JTextComponent; puede encontrar un ejemplo de uso en la carpeta src / test.

agregue estas líneas a private void addKeyBindingToRequestFocusInPopUpWindow () de primera respuesta para ejecutar la tecla ARRIBA. Él responde es perfecto.

 //here I have to do my code for up key //--------------------------------------------------------------------- //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released"); textField.getActionMap().put("Up released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) {//focuses the first label on popwindow for (int i = suggestionsPanel.getComponentCount()-1; i >=0; i--) { if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) { ((SuggestionLabel) suggestionsPanel.getComponent(i)).setFocused(true); autoSuggestionPopUpWindow.toFront(); autoSuggestionPopUpWindow.requestFocusInWindow(); suggestionsPanel.requestFocusInWindow(); suggestionsPanel.getComponent(i).requestFocusInWindow(); break; } } } }); suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released"); suggestionsPanel.getActionMap().put("Up released", new AbstractAction() { //######int lastFocusableIndex = 0; int lastFocusableIndex = 0; //lastFocusableIndex=lastFocusableIndex___; @Override public void actionPerformed(ActionEvent ae) {//allows scrolling of labels in pop window (I know very hacky for now :)) ArrayList sls = getAddedSuggestionLabels(); int max = sls.size(); lastFocusableIndex=lastFocusableIndex___; System.out.println("UP UP UP UP");//***// System.out.println("max = "+String.valueOf(max));//***// System.out.println("lastFocusableIndex = "+String.valueOf(lastFocusableIndex));//***// System.out.println("UP UP UP UP");//***// if (max > 1) {//more than 1 suggestion for (int i = max-1; i >=0; i--) { SuggestionLabel sl = sls.get(i); if (sl.isFocused()) { if (lastFocusableIndex == 0) { lastFocusableIndex = max - 1; lastFocusableIndex___=lastFocusableIndex; sl.setFocused(false); autoSuggestionPopUpWindow.setVisible(false); setFocusToTextField(); checkForAndShowSuggestions();//fire method as if document listener change occured and fired it } else { sl.setFocused(false); lastFocusableIndex = i; lastFocusableIndex___=lastFocusableIndex; } } else if (lastFocusableIndex > i) { if (i < max ) { sl.setFocused(true); autoSuggestionPopUpWindow.toFront(); autoSuggestionPopUpWindow.requestFocusInWindow(); suggestionsPanel.requestFocusInWindow(); suggestionsPanel.getComponent(i).requestFocusInWindow(); lastFocusableIndex = i; lastFocusableIndex___=lastFocusableIndex; break; } } } } else {//only a single suggestion was given autoSuggestionPopUpWindow.setVisible(false); setFocusToTextField(); checkForAndShowSuggestions();//fire method as if document listener change occured and fired it } } }); 

Un enfoque de trabajo que utilicé en un proyecto fue poner un JTextField encima de un JComboBox y abrir el cuadro combinado subyacente mientras escribes en JTextField usando un detector de documentos. Es probable que desee un modelo de cuadro combinado personalizado para permitir cambiar los elementos de forma más eficiente ya que el modelo predeterminado, creo que solo permite agregar elementos de uno en uno, lo que puede ser un golpe de rendimiento. para abrir el cuadro combinado creo que hay un método para mostrarlo si obtienes su UI. Me encontré con un par de errores con el desplazamiento cuando intenté cambiar los elementos mientras estaba abierto, por lo que es posible que tenga que cerrarlo, cambiar los elementos y volver a mostrar. Para cosas de teclado, puede capturar las pulsaciones de tecla del teclado en JTextField y llamar a JComboBox de manera apropiada.