Criar uma checkbox de texto de autocomplete em Java com uma lista suspensa

Eu quero criar uma checkbox de texto de sugestão automática que consultaria o database em cada evento de lançamento de chave. Essa parte é fácil, mas eu quero dar um bom visual a ela. Algo semelhante à checkbox de texto de sugestão automática que vemos em sites como a pesquisa no Facebook.

Como fazer essa interface?

Uma ideia ingênua seria ter um JList colocado logo abaixo da checkbox de texto e defini-lo visível com os resultados nele ao encontrar um.

Alguma idéia melhor ou uma maneira padrão de fazer isso?

A resposta do @ syb0rg é mais fácil, pois usa uma biblioteca de terceiros.

No entanto, usei uma abordagem alternativa:

Ele usa uma class personalizada chamada AutoSuggestor que aceita um JTextField , sua Window uma ArrayList de palavras para conferir palavras digitadas, uma cor de fundo e cor de texto e uma cor de foco de sugestão, além de um valor de opacidade. Ao passar a referência JTextField um DocumentListener é adicionado, que fará o trabalho de verificar qual palavra é digitada e se deve ou não exibir sugestões e, em caso afirmativo, quais sugestões exibir. Quando uma palavra é digitada, o DocumentListener triggersrá o wordTyped(String wordTyped) com a palavra atual sendo digitada ou (pelo menos, como a palavra já foi digitada), em wordTyped(..) a palavra será verificada em relação àqueles em o dictionary de classs do AutoSuggestor , que é uma ArrayList básica de String pode ser configurado na hora, como visto no exemplo abaixo:

insira a descrição da imagem aqui

(Por enquanto você terá que usar o mouse e clicar na palavra que deseja completar automaticamente, ou usar DOWN para transversar sugestões e o campo de texto e ENTER para selecionar a sugestão ao percorrer usando a tecla para baixo . Eu ainda não implementei 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á, as únicas adições necessárias possíveis são:

  • Tecla PARA CIMA Traveza de foco para itens dentro da checkbox de auto-sugestões de popup para que possamos seguir em sentido ascendente.

Se houver algum problema, deixe-me saber o que posso fazer. Mas parece estar funcionando bem (toque de madeira).

Uma maneira muito fácil de fazer isso é usar a implementação de preenchimento automático de GlazedList . É muito fácil de se levantar e correr. Você pode encontrá-lo aqui .

Você pode instalar o preenchimento automático em um JComboBox com apenas uma linha de código Glazed, assim:

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

O SwingX também suporta o SwingX automático e pode ser mais fácil de usar do que o GlazedList . Tudo o que você escreve com o SwingX é AutoCompleteDecorator.decorate(comboBox);

Para usar a class TextAutoCompleter, você precisa baixar um arquivo jar AutoCompleter.jar e adicioná-lo à pasta de biblioteca do seu projeto e aqui está o link para download: http://download1689.mediafire.com/4grrthscpsug/7pwzgefiomu392o/AutoCompleter.jar -Nawin

// Na class Main, escreva o seguinte 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"); } 

}

Eu queria auto-completar para o editor no meu AVR assembler IDE, então eu escrevi uma implementação que funciona como o preenchimento automático no Eclipse (ativação de CTRL-SPACE, lista suspensa com barras de rolagem, teclas de cursor + mouse de navegação). Não tem dependencies externas e é apenas uma única class. Deve funcionar para todas as subclasss de JTextComponent; Você pode encontrar um exemplo de uso na pasta src / test.

adicione estas linhas ao private void addKeyBindingToRequestFocusInPopUpWindow () da primeira resposta inorder para implementar a tecla UP. Ele responde é perfeito.

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

Uma abordagem de trabalho que usei em um projeto foi colocar um JTextField sobre um JComboBox e tornar a checkbox de combinação subjacente aberta enquanto você digita no JTextField usando um listener de documento. Você provavelmente quer um modelo de checkbox de combinação personalizado para permitir a mudança dos itens de forma mais eficiente, já que o modelo padrão só permite que itens sejam adicionados um de cada vez, o que pode ser um impacto no desempenho. Para abrir a checkbox de combinação, acho que há um método para mostrá-la se você obtiver sua UI. Eu encontrei um monte de bugs com a rolagem quando tentei alterar os itens enquanto ele estava aberto, então você pode precisar fechá-lo, alterar os itens e fazer novas apresentações. Para material de teclado, você pode pegar as teclas pressionadas do teclado no JTextField e chamar o JComboBox adequadamente.