Crea una casella di testo di autocompletamento in Java con un elenco a discesa

Voglio creare una casella di testo di suggerimento automatico che interrogherà il database ad ogni evento di rilascio chiave. Quella parte è facile, ma voglio dare una bella visuale ad essa. Qualcosa di simile alla casella di testo di suggerimento automatico che vediamo in siti Web come la ricerca in Facebook.

Come creare una simile interfaccia?

Un’idea ingenua sarebbe quella di avere una JList posizionata appena sotto la casella di testo e renderla visibile con i risultati al suo interno trovandone una.

Qualche idea migliore o un modo standard per farlo?

La risposta di @ syb0rg è più semplice, in quanto utilizza una libreria di terze parti.

Tuttavia ho usato un approccio alternativo:

Utilizza una class personalizzata chiamata AutoSuggestor che accetta un JTextField , la sua Window un ArrayList di parole per controllare le parole digitate, un colore di sfondo e il colore del testo e il colore di messa a fuoco di suggerimento, nonché un valore di opacità. Passando il riferimento JTextField viene aggiunto un DocumentListener che farà il lavoro di controllare quale parola è stata digitata e se visualizzare i suggerimenti o meno e in tal caso quali suggerimenti visualizzare. Quando viene digitata una parola, DocumentListener wordTyped(String wordTyped) metodo wordTyped(String wordTyped) con la parola corrente digitata o (almeno quanto mai della parola è stata digitata), in wordTyped(..) la parola verrà confrontata con quelli in il dizionario delle classi di AutoSuggestor che è un ArrayList di String di base può essere impostato al volo come mostrato nell’esempio seguente:

inserisci la descrizione dell'immagine qui

(Per ora dovrai utilizzare il mouse e fare clic sulla parola che desideri venga completata automaticamente, oppure utilizzare DOWN per i suggerimenti trasversali e il campo di testo e INVIO per selezionare il suggerimento quando attraversi usando il tasto giù . Non ho ancora implementato 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 actionPerformsd(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 + " "); } } 

Così com'è, le uniche aggiunte possibili IMO sono:

  • Tasto SU Consente di evidenziare la traversablità per gli elementi all'interno della casella di controllo automatico popup in modo che possiamo andare verso l'alto.

Se ci sono dei nodes, sappi che vedrò cosa posso fare. Ma sembra funzionare bene (touch di legno).

Un modo davvero semplice per farlo è utilizzare l’implementazione di completamento automatico di GlazedList . È molto facile mettersi al lavoro. Puoi trovarlo qui .

Puoi installare il completamento automatico su un JComboBox con una sola riga di codice Glazed, come questo:

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

SwingX supporta anche il SwingX automatico e potrebbe essere più facile da usare rispetto a GlazedList . Tutto ciò che scrivi con SwingX è SwingX AutoCompleteDecorator.decorate(comboBox);

Per utilizzare la class TextAutoCompleter è necessario scaricare un file jar AutoCompleter.jar e aggiungerlo alla cartella della libreria del progetto ed ecco il link per il download: http://download1689.mediafire.com/4grrthscpsug/7pwzgefiomu392o/AutoCompleter.jar -Nawin

// Nella class Main scrivi il seguente codice

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

}

Volevo il completamento automatico per l’editor nel mio IDE di assembler AVR, quindi ho scritto un’implementazione che funziona proprio come il completamento automatico in Eclipse (triggerszione CTRL-SPACE, elenco a discesa con barre di scorrimento, tasti cursore + navigazione del mouse). Non ha dipendenze esterne ed è solo una singola class. Dovrebbe funzionare per tutte le sottoclassi di JTextComponent; puoi trovare un esempio di utilizzo nella cartella src / test.

aggiungi queste righe al vuoto privato addKeyBindingToRequestFocusInPopUpWindow () della prima risposta per implementare la chiave UP. Lui risponde è perfetto.

 //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 actionPerformsd(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 actionPerformsd(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 approcio di lavoro che ho usato su un progetto è stato quello di mettere un JTextField su un JComboBox e rendere aperta la casella combinata sottostante durante la digitazione in JTextField usando un listener di documenti. Probabilmente vorrai un modello di combobox personalizzato per consentire di modificare gli articoli in modo più efficace, dal momento che il modello predefinito consente solo l’aggiunta di elementi alla volta, il che può comportare un impatto sulle prestazioni. per aprire la casella combinata penso che ci sia un metodo per mostrarlo se ottieni la sua interfaccia utente. Mi sono imbattuto in un paio di bug con lo scrolling quando ho provato a cambiare gli oggetti mentre era aperto, quindi potrebbe essere necessario chiuderlo, modificare gli elementi e reshow. Per quanto riguarda le tastiere, è ansible prendere le pressioni dei tasti della tastiera in JTextField e richiamare appropriatamente in JComboBox.