【问题标题】:Create a autocompleting textbox in Java with a dropdown list使用下拉列表在 Java 中创建一个自动完成文本框
【发布时间】:2012-12-20 15:37:58
【问题描述】:

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

如何制作这样的界面?

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

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

【问题讨论】:

  • 没有下拉菜单!!这是可以做到的。问题是如何根据结果的数量使下拉列表的大小也可变。
  • @jairaj 无论使用 JTextField 可以实现什么,都可以轻松转换为 JComboBox。您只需将JTextField 设置为JCombobox 的ComboBoxEditor,并使JComboBox 可编辑。顺便说一句,我会看看 SwingX,它已经有一个相当不错的自动完成组件(看看谷歌)

标签: java swing autocomplete autosuggest


【解决方案1】:

建立在Davidsolution之上:

我为 UP 键添加了功能,并为 ESC 键添加了功能以隐藏弹出窗口。 In addition to that, you can specify a callback function when constructing a AutoSuggestor object, which will be called when a suggestion from the list is selected.

import javax.swing.border.LineBorder
import java.util.ArrayList
import javax.swing.event.DocumentListener

import java.awt.*
import java.awt.event.*
import javax.swing.*
import javax.swing.event.DocumentEvent

/**
 * Author of the original version: David @ https://stackoverflow.com/users/1133011/david-kroukamp
 */
class Test {
    init {

        val frame = JFrame()
        frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE


        val f = JTextField(10)

        val autoSuggestor = object : AutoSuggestor(f, frame, ArrayList(), Color.WHITE.brighter(), Color.BLUE, Color.RED, 0.75f) {
            override fun wordTyped(typedWord: String?): Boolean {

                //create list for dictionary this in your case might be done via calling a method which queries db and returns results as arraylist
                val words = ArrayList<String>()
                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
            }
        }

        val p = JPanel()

        p.add(f)

        frame.add(p)

        frame.pack()
        frame.isVisible = true
    }

    companion object {

        @JvmStatic
        fun main(args: Array<String>) {
            SwingUtilities.invokeLater { Test() }
        }
    }
}

internal open class AutoSuggestor(val textField: JTextField, val container: Window, words: ArrayList<String>, popUpBackground: Color, private val suggestionsTextColor: Color, private val suggestionFocusedColor: Color, opacity: Float, private val callback: (String) -> Unit = {}) {
    private val suggestionsPanel: JPanel
    val autoSuggestionPopUpWindow: JWindow
    private var typedWord: String? = null
    private val dictionary = ArrayList<String>()
    private var currentIndexOfSpace: Int = 0
    private var tW: Int = 0
    private var tH: Int = 0
    private val documentListener = object : DocumentListener {
        override fun insertUpdate(de: DocumentEvent) {
            checkForAndShowSuggestions()
        }

        override fun removeUpdate(de: DocumentEvent) {
            checkForAndShowSuggestions()
        }

        override fun changedUpdate(de: DocumentEvent) {
            checkForAndShowSuggestions()
        }
    }

    val addedSuggestionLabels: ArrayList<SuggestionLabel>
        get() {
            val sls = ArrayList<SuggestionLabel>()
            for (i in 0 until suggestionsPanel.componentCount) {
                if (suggestionsPanel.getComponent(i) is SuggestionLabel) {
                    val sl = suggestionsPanel.getComponent(i) as SuggestionLabel
                    sls.add(sl)
                }
            }
            return sls
        }

    //get newest word after last white space if any or the first word if no white spaces
    val currentlyTypedWord: String
        get() {
            val text = textField.text
            var wordBeingTyped = ""
            if (text.contains(" ")) {
                val tmp = text.lastIndexOf(" ")
                if (tmp >= currentIndexOfSpace) {
                    currentIndexOfSpace = tmp
                    wordBeingTyped = text.substring(text.lastIndexOf(" "))
                }
            } else {
                wordBeingTyped = text
            }
            return wordBeingTyped.trim { it <= ' ' }
        }

    init {
        this.textField.document.addDocumentListener(documentListener)

        setDictionary(words)

        typedWord = ""
        currentIndexOfSpace = 0
        tW = 0
        tH = 0

        autoSuggestionPopUpWindow = JWindow(container)
        autoSuggestionPopUpWindow.opacity = opacity

        suggestionsPanel = JPanel()
        suggestionsPanel.layout = GridLayout(0, 1)
        suggestionsPanel.background = popUpBackground

        addFocusListenersToHandleVisibilityOfPopUpWindow()
        addKeyBindingToRequestFocusInPopUpWindow()
    }

    private fun addFocusListenersToHandleVisibilityOfPopUpWindow() {
        textField.addFocusListener(object:FocusListener {
            override fun focusLost(e: FocusEvent?) {
                var focusOnPopUp = false
                for (i in 0 until suggestionsPanel.componentCount) {
                    if (suggestionsPanel.getComponent(i) is SuggestionLabel) {
                        val label = suggestionsPanel.getComponent(i) as SuggestionLabel
                        if (label.isFocused)
                            focusOnPopUp = true
                    }
                }

                if (!focusOnPopUp && !shouldShowPopUpWindow) {
                    autoSuggestionPopUpWindow.isVisible = false
                }
            }
            override fun focusGained(e: FocusEvent?) {
                shouldShowPopUpWindow = false
            }
        })
    }

    private fun addKeyBindingToRequestFocusInPopUpWindow() {
        textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), "Escape released")
        textField.actionMap.put("Escape released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {// Hide the popwindow
                shouldShowPopUpWindow = false
                autoSuggestionPopUpWindow.isVisible = false
            }
        })


        textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released")
        textField.actionMap.put("Down released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {//focuses the first label on popwindow
                for (i in 0 until suggestionsPanel.componentCount) {
                    if (suggestionsPanel.getComponent(i) is SuggestionLabel) {
                        (suggestionsPanel.getComponent(i) as SuggestionLabel).isFocused = true
                        autoSuggestionPopUpWindow.toFront()
                        autoSuggestionPopUpWindow.requestFocusInWindow()
                        suggestionsPanel.requestFocusInWindow()
                        suggestionsPanel.getComponent(i).requestFocusInWindow()
                        break
                    }
                }
            }
        })

        textField.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released")
        textField.actionMap.put("Up released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {//focuses the last label on popwindow
                for (i in 0 until suggestionsPanel.componentCount) {
                    val reverseIndex = suggestionsPanel.componentCount-1 - i
                    if (suggestionsPanel.getComponent(reverseIndex) is SuggestionLabel) {
                        (suggestionsPanel.getComponent(reverseIndex) as SuggestionLabel).isFocused = true
                        autoSuggestionPopUpWindow.toFront()
                        autoSuggestionPopUpWindow.requestFocusInWindow()
                        suggestionsPanel.requestFocusInWindow()
                        suggestionsPanel.getComponent(reverseIndex).requestFocusInWindow()
                        break
                    }
                }
            }
        })

        suggestionsPanel.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), "Escape released")
        suggestionsPanel.actionMap.put("Escape released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {// Hide the popwindow
                shouldShowPopUpWindow = false
                autoSuggestionPopUpWindow.isVisible = false
            }
        })
        suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "Up released")
        suggestionsPanel.actionMap.put("Up released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {//allows scrolling of labels in pop window (I know very hacky for now :))

                val sls = addedSuggestionLabels
                val max = sls.size

                var indexOfFocusedSuggestion = -1
                for (i in 0 until max) {
                    val sl = sls[i]
                    if ( sl.isFocused )
                        indexOfFocusedSuggestion = i
                }

                if (indexOfFocusedSuggestion - 1 < 0) {
                    sls[indexOfFocusedSuggestion].isFocused = false
                    autoSuggestionPopUpWindow.isVisible = false
                    setFocusToTextField()
                    checkForAndShowSuggestions()//fire method as if document listener change occured and fired it
                }
                else {
                    sls[indexOfFocusedSuggestion].isFocused = false
                    sls[indexOfFocusedSuggestion-1].isFocused = true
                    autoSuggestionPopUpWindow.toFront()
                    autoSuggestionPopUpWindow.requestFocusInWindow()
                    suggestionsPanel.requestFocusInWindow()
                    suggestionsPanel.getComponent(indexOfFocusedSuggestion-1).requestFocusInWindow()
                }

            }
        })
        suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released")
        suggestionsPanel.actionMap.put("Down released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {//allows scrolling of labels in pop window (I know very hacky for now :))

                val sls = addedSuggestionLabels
                val max = sls.size

                var indexOfFocusedSuggestion = -1
                for (i in 0 until max) {
                    val sl = sls[i]
                    if ( sl.isFocused )
                        indexOfFocusedSuggestion = i
                }

                if (indexOfFocusedSuggestion + 1 >= max) {
                    sls[indexOfFocusedSuggestion].isFocused = false
                    autoSuggestionPopUpWindow.isVisible = false
                    setFocusToTextField()
                    checkForAndShowSuggestions()//fire method as if document listener change occured and fired it
                }
                else {
                    sls[indexOfFocusedSuggestion].isFocused = false
                    sls[indexOfFocusedSuggestion+1].isFocused = true
                    autoSuggestionPopUpWindow.toFront()
                    autoSuggestionPopUpWindow.requestFocusInWindow()
                    suggestionsPanel.requestFocusInWindow()
                    suggestionsPanel.getComponent(indexOfFocusedSuggestion+1).requestFocusInWindow()
                }
            }

        })
    }

    private fun setFocusToTextField() {
        container.toFront()
        container.requestFocusInWindow()
        textField.requestFocusInWindow()
    }

    var shouldShowPopUpWindow = false

    private fun checkForAndShowSuggestions() {
        typedWord = currentlyTypedWord

        suggestionsPanel.removeAll()//remove previos words/jlabels that were added

        //used to calcualte size of JWindow as new Jlabels are added
        tW = 0
        tH = 0

        val added = wordTyped(typedWord)

        if (!added) {
            if (autoSuggestionPopUpWindow.isVisible) {
                autoSuggestionPopUpWindow.isVisible = false
            }
        } else {
            shouldShowPopUpWindow = true
            showPopUpWindow()
            setFocusToTextField()
        }
    }

    protected fun addWordToSuggestions(word: String) {
        val suggestionLabel = SuggestionLabel(word, suggestionFocusedColor, suggestionsTextColor, this, callback)

        calculatePopUpWindowSize(suggestionLabel)

        suggestionsPanel.add(suggestionLabel)
    }

    private fun calculatePopUpWindowSize(label: JLabel) {
        //so we can size the JWindow correctly
        if (tW < label.preferredSize.width) {
            tW = label.preferredSize.width
        }
        tH += label.preferredSize.height
    }

    private fun showPopUpWindow() {
        autoSuggestionPopUpWindow.contentPane.add(suggestionsPanel)
        autoSuggestionPopUpWindow.minimumSize = Dimension(textField.width, 30)
        autoSuggestionPopUpWindow.setSize(tW, tH)
        autoSuggestionPopUpWindow.isVisible = true

        var windowX = 0
        var windowY = 0

        windowX = container.getX() + textField.x + 5
        if (suggestionsPanel.height > autoSuggestionPopUpWindow.minimumSize.height) {
            windowY = container.getY() + textField.y + textField.height + autoSuggestionPopUpWindow.minimumSize.height
        } else {
            windowY = container.getY() + textField.y + textField.height + autoSuggestionPopUpWindow.height
        }

        autoSuggestionPopUpWindow.setLocation(windowX, windowY)
        autoSuggestionPopUpWindow.minimumSize = Dimension(textField.width, 30)
        autoSuggestionPopUpWindow.revalidate()
        autoSuggestionPopUpWindow.repaint()

    }

    fun setDictionary(words: ArrayList<String>?) {
        dictionary.clear()
        if (words == null) {
            return //so we can call constructor with null value for dictionary without exception thrown
        }
        for (word in words) {
            dictionary.add(word)
        }
    }

    fun addToDictionary(word: String) {
        dictionary.add(word)
    }

    open fun wordTyped(typedWord: String?): Boolean {

        if (typedWord!!.isEmpty()) {
            return false
        }

        var suggestionAdded = false

        for (word in dictionary) {//get words in the dictionary which we added
            var fullyMatches = word.length >= typedWord.length
            for (i in 0 until typedWord.length) {//each string in the word
                if (word.length > i && !typedWord.toLowerCase().startsWith(word.toLowerCase()[i].toString(), i)) {//check for match
                    fullyMatches = false
                    break
                }
            }
            if (fullyMatches) {
                addWordToSuggestions(word)
                suggestionAdded = true
            }
        }
        return suggestionAdded
    }
}

internal class SuggestionLabel(string: String, private val suggestionBorderColor: Color, private val suggestionsTextColor: Color, private val autoSuggestor: AutoSuggestor, private val callback: (String) -> Unit) : JLabel(string) {

    var isFocused = false
        set(focused) {
            if (focused) {
                border = LineBorder(suggestionBorderColor)
            } else {
                border = null
            }
            repaint()
            field = focused
        }
    private val autoSuggestionsPopUpWindow: JWindow
    private val textField: JTextField

    init {
        this.textField = autoSuggestor.textField
        this.autoSuggestionsPopUpWindow = autoSuggestor.autoSuggestionPopUpWindow

        initComponent()
    }

    private fun initComponent() {
        isFocusable = true
        foreground = suggestionsTextColor

        addMouseListener(object : MouseAdapter() {
            override fun mouseClicked(me: MouseEvent) {
                super.mouseClicked(me)

                replaceWithSuggestedText()

                autoSuggestionsPopUpWindow.isVisible = false
            }
        })

        getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, true), "Enter released")
        actionMap.put("Enter released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {
                replaceWithSuggestedText()
                autoSuggestionsPopUpWindow.isVisible = false
            }
        })

        getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true), "Escape released")
        actionMap.put("Escape released", object : AbstractAction() {
            override fun actionPerformed(ae: ActionEvent) {// Hide the popwindow
                autoSuggestionsPopUpWindow.isVisible = false
            }
        })
    }

    private fun replaceWithSuggestedText() {

        val suggestedWord = text
        val text = textField.text
        val typedWord = autoSuggestor.currentlyTypedWord
        val t = text.substring(0, text.lastIndexOf(typedWord))
        val tmp = t + text.substring(text.lastIndexOf(typedWord)).replace(typedWord, suggestedWord)
        textField.text = tmp
        callback(tmp)
    }
}

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

          }

          【讨论】:

          • khawatiwada 感谢您的建议。我在网上搜索并偶然发现了这一点。我想将它与诸如 WAMP 之类的 HTTP 服务器一起使用,我将在其中通过 RESTFul API 调用我的字符串。您的明确示例中只有一个问题:我如何使用在文本字段中键入的字符串作为 serach 参数?在你上面的例子中,我真的不清楚。谢谢
          【解决方案6】:

          @syb0rg 的回答更简单,因为它使用了第 3 方库。

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

          它使用一个名为AutoSuggestor 的自定义类,它接受JTextField、它的WindowArrayList&lt;String&gt; 用于检查输入的单词、背景颜色和文本颜色、建议焦点颜色以及不透明度值。通过传递JTextField 引用添加了DocumentListener,它将检查输入的单词以及是否显示建议以及如果是则显示什么建议。当输入一个词时,DocumentListener 将触发wordTyped(String wordTyped) 方法,当前输入的词或(至少输入了多少个词),在wordTyped(..) 中,该词将与@ 中的词进行检查987654331@s classes dictionary 这是String 的基本ArrayList 可以动态设置,如下例所示:

          (现在你必须用鼠标点击你想自动完成的单词,或者使用DOWN横向建议和文本域使用 down 键进行遍历时,请按 ENTER 选择建议。我还没有实现 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<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 + " ");
              }
          }
          

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

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

          如果有任何问题,请让我知道,我会看看我能做什么。但似乎运行良好(触摸木头)。

          【讨论】:

          • 好一个!但是当我键入时,焦点从文本字段中丢失了。我必须再次单击文本字段才能再次开始输入。因此,对于我输入的每个字符,我必须强制专注于文本字段和类型。你遇到过这个问题吗?如果是,你修好了吗?
          • @arunram 同样的事情发生在我身上。此代码在 Windows 7 上完美运行,但在 Machintosh OS X Yosemite (10.10.2) 上却没有。我仍在尝试解决此问题,但我不是专家,因此这可能需要很长时间。没有承诺,但我会尽力而为。
          • 我最喜欢这个解决方案。其他的更优雅,但这个更容易破解和学习
          • 我在运行这段代码时遇到了几个问题。文本区域窗口在键入时失去焦点并中断。程序也经常崩溃。您能否修复您的代码或提出一些可能的原因?
          • 如果有人在使用它并且在向框中写入文本时遇到问题,则添加条件为 (int i = 0; i
          【解决方案7】:

          一个非常简单的方法是使用自动完成的GlazedList 实现。启动和运行非常容易。你可以找到它here

          您可以在 JComboBox 上安装自动完成功能,只需一行 Glazed 代码,如下所示:

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

          另外SwingX 支持自动完成,可能比GlazedList 更易于使用。你用SwingX写的都是AutoCompleteDecorator.decorate(comboBox);

          【讨论】:

          • " 你用 SwingX 写的只是 AutoCompleteDecorator.decorate(comboBox);"很好的答案!!!为什么我在 4 天后才发现这个?
          • 您的代码出现此错误:AutoCompleteSupport must be accessed from the Swing Event Dispatch Thread.
          • 我找不到 GlazedLists.eventListOf 的 jar 文件,你能帮我找到一个 jar 文件吗?
          • @syb0rg :+1。我找到的最简单的答案。但是如何从数据库中做同样的事情。如果您对此提出建议,我将不胜感激
          • 将数据库结果存储在一个数组中:)
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-01-20
          • 2014-01-14
          • 2013-06-15
          • 1970-01-01
          • 1970-01-01
          • 2011-05-12
          • 1970-01-01
          相关资源
          最近更新 更多