使用下拉列表在Java中创建自动完成文本框

我想创建一个自动建议文本框,它将在每个密钥释放事件中查询数据库。 那部分很简单,但我想给它一个很好的视觉效果。 类似于我们在Facebook搜索等网站中看到的自动建议文本框。

如何制作这样的界面?

一个天真的想法是将JList放在文本框的正下方,并将其设置为可见,并在其中找到一个结果。

这样做有更好的想法或标准方法吗?

@ syb0rg的答案更简单,因为它使用第三方库。

但是我使用了另一种方法:

它使用一个名为AutoSuggestor的自定义类,它接受一个JTextField ,它的Window是一个ArrayList ,用于检查键入的单词,背景颜色和文本颜色,建议焦点颜色以及不透明度值。 通过传递JTextField引用,添加了一个DocumentListener ,它将执行检查键入的单词以及是否显示建议的工作,如果是,则显示哪些建议。 当输入一个单词时, DocumentListener将触发wordTyped(String wordTyped)方法,当前输入的单词或者(至少输入了多少单词),在wordTyped(..)中将对照那些单词进行检查。 AutoSuggestor的类字典String的基本ArrayList ,可以动态设置,如下例所示:

在此处输入图像描述

(现在你必须使用鼠标并单击你想要自动完成的单词,或者使用DOWN来横向建议和textfieldENTER来选择使用向下键遍历时的建议。我还没有实现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 + " "); } } 

目前唯一可能需要的补充IMO是:

  • 向上键弹出自动建议框中项目的焦点可穿越性,这样我们就可以向上移动。

如果有任何纠结,我知道我能做什么。 但似乎运行良好(触摸木材)。

一个非常简单的方法是使用自动完成的GlazedList实现。 起床和跑步非常容易。 你可以在这里找到它。

您可以在只有一行Glazed代码的JComboBox上安装auto-complete,如下所示:

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

此外, SwingX支持自动完成,可能比GlazedList更容易使用。 你用SwingX编写的所有内容都是AutoCompleteDecorator.decorate(comboBox);

要使用TextAutoCompleter类,您需要下载一个jar文件AutoCompleter.jar并将其添加到项目的库文件夹中,这里是下载链接: http : //download1689.mediafire.com/4grrthscpsug/7pwzgefiomu392o/AutoCompleter.jar -Nawin

//在Main类中编写以下代码

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

}

我想在我的AVR汇编器IDE中自动完成编辑器,所以我编写了一个实现 ,就像Eclipse中的自动完成一样(CTRL-SPACE激活,带滚动条的下拉列表,光标键+鼠标导航)。 它没有外部依赖关系,只是一个类。 它应该适用于所有JTextComponent子类; 你可以在src / test文件夹中找到一个用法示例。

将此行添加到第一个答案的private void addKeyBindingToRequestFocusInPopUpWindow()以实现UP键。 他的回答是完美的。

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

我在项目中使用的一个工作approch是将JTextField放在JComboBox之上,并在使用文档侦听器键入JTextField时打开底层combobox。 您可能需要一个自定义combobox模型,以允许更有效地更改项目作为默认模型我认为只允许一次添加一个项目,这可能会影响性能。 打开combobox我认为有一种方法可以显示它,如果你得到它的UI。 当我尝试在打开项目时更改项目时,我遇到了一些滚动错误,因此您可能需要关闭它,更改项目并重新显示。 对于键盘内容,您可以在JTextField中捕获键盘按键并适当调用JComboBox。