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

时间:2013-01-06 21:28:21

标签: java swing autocomplete autosuggest

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

如何制作这样的界面?

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

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

7 个答案:

答案 0 :(得分:42)

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

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

它使用名为AutoSuggestor的自定义类,该类接受JTextField,其WindowArrayList<String>个字来检查打字的字词,背景颜色和文字颜色,和建议焦点颜色以及不透明度值。通过传递JTextField引用,添加DocumentListener将执行检查键入的内容以及是否显示建议的工作,如果是,则显示哪些建议。输入单词后,DocumentListener将触发wordTyped(String wordTyped)方法,并输入当前单词或(至少输入了多少单词),wordTyped(..)单词将为AutoSuggestor检查ArrayList词典中{{1>} String 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<String> 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<String> 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<String> 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<SuggestionLabel> 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<SuggestionLabel> getAddedSuggestionLabels() { ArrayList<SuggestionLabel> 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<String> 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 + " "); } } 的{​​{1}}中的{{1}},可以动态设置,如下例所示:< / p>

enter image description here

(现在您必须使用鼠标并单击要自动完成的单词,或使用 DOWN 来横向建议和 textfield 输入以在使用 down 键进行遍历时选择建议。我还没有实现 UP

{{1}}

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

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

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

答案 1 :(得分:27)

执行此操作的一种非常简单的方法是使用自动完成的GlazedList实现。起床和跑步非常容易。你可以找到它here

您可以在只有一行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);

答案 2 :(得分:2)

要使用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");

}

}

答案 3 :(得分:1)

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

答案 4 :(得分:1)

建立在Davidsolution之上:

我为 UP 键添加了功能,并为 ESC 键添加了功能,以隐藏弹出窗口。 除此之外,您可以在构造getFaults() { console.log("click"); this.faultsService .getFaults() .subscribe(data => { let faults = Fault[]; data.forEach(object => { faults.push({description: object.description}) }); console.log(faults); }); } 对象时指定一个回调函数,当从列表中选择建议时将调用该函数。

AutoSuggestor

注意:上面的代码是用Kotlin写的,但是如果您真的想要Java代码,则可以轻松地convert it back to Java

答案 5 :(得分:0)

将此行添加到第一个答案的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<SuggestionLabel> 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
            }
        }
    });

答案 6 :(得分:0)

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