使用下拉列表在 Java 中创建一个自动完成文本框
Posted
技术标签:
【中文标题】使用下拉列表在 Java 中创建一个自动完成文本框【英文标题】:Create a autocompleting textbox in Java with a dropdown list 【发布时间】:2012-12-20 15:37:58 【问题描述】:我想创建一个自动建议文本框,它会在每次按键释放事件时查询数据库。 那部分很简单,但我想给它一个漂亮的视觉效果。类似于我们在 Facebook 搜索等网站上看到的自动建议文本框。
如何制作这样的界面?
一个天真的想法是在文本框正下方放置一个 JList,并将其设置为可见,并在找到它时显示结果。
有更好的想法或标准的方法吗?
【问题讨论】:
Have a look at this existing answer 没有下拉菜单!!这是可以做到的。问题是如何根据结果的数量使下拉列表的大小也可变。 @jairaj 无论使用 JTextField 可以实现什么,都可以轻松转换为 JComboBox。您只需将JTextField
设置为JCombobox 的ComboBoxEditor,并使JComboBox 可编辑。顺便说一句,我会看看 SwingX,它已经有一个相当不错的自动完成组件(看看谷歌)
【参考方案1】:
建立在David的solution之上:
我为 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://***.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
、它的Window
和ArrayList<String>
用于检查输入的单词、背景颜色和文本颜色、建议焦点颜色以及不透明度值。通过传递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。我找到的最简单的答案。但是如何从数据库中做同样的事情。如果您对此提出建议,我将不胜感激
将数据库结果存储在一个数组中:)以上是关于使用下拉列表在 Java 中创建一个自动完成文本框的主要内容,如果未能解决你的问题,请参考以下文章