如何在JTextArea swing中实现autosugesion

如果你有人回答,请问我这个问题。 基本上需要像谷歌搜索引擎,当我们按任意键然后它将显示建议相关的按键。

关于Satish Dhiman

从我的评论/ 以前的代码中看到此更新:

JTextFieldAutoSuggestor一起使用:

在此处输入图像描述

使用JTextArea (或JTextField之外的任何其他JTextComponent将导致弹出窗口显示在插入符号下)和AutoSuggestor

在此处输入图像描述

 import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Rectangle; 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.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextArea; 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; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; /** * @author David */ public class Test { public Test() { JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //JTextField f = new JTextField(10); JTextArea f = new JTextArea(10, 10); //JEditorPane f = new JEditorPane(); //create words for dictionary could also use null as parameter for AutoSuggestor(..,..,null,..,..,..,..) and than call AutoSuggestor#setDictionary after AutoSuggestr insatnce has been created ArrayList words = new ArrayList<>(); words.add("hello"); words.add("heritage"); words.add("happiness"); words.add("goodbye"); words.add("cruel"); words.add("car"); words.add("war"); words.add("will"); words.add("world"); words.add("wall"); AutoSuggestor autoSuggestor = new AutoSuggestor(f, frame, words, Color.WHITE.brighter(), Color.BLUE, Color.RED, 0.75f) { @Override boolean wordTyped(String typedWord) { System.out.println(typedWord); return super.wordTyped(typedWord);//checks for a match in dictionary and returns true or false if found or not } }; 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 JTextComponent textComp; private final Window container; private JPanel suggestionsPanel; private JWindow autoSuggestionPopUpWindow; private String typedWord; private final ArrayList dictionary = new ArrayList<>(); private int currentIndexOfSpace, tW, tH; private DocumentListener documentListener = new DocumentListener() { @Override public void insertUpdate(DocumentEvent de) { checkForAndShowSuggestions(); } @Override public void removeUpdate(DocumentEvent de) { checkForAndShowSuggestions(); } @Override public void changedUpdate(DocumentEvent de) { checkForAndShowSuggestions(); } }; private final Color suggestionsTextColor; private final Color suggestionFocusedColor; public AutoSuggestor(JTextComponent textComp, Window mainWindow, ArrayList words, Color popUpBackground, Color textColor, Color suggestionFocusedColor, float opacity) { this.textComp = textComp; this.suggestionsTextColor = textColor; this.container = mainWindow; this.suggestionFocusedColor = suggestionFocusedColor; this.textComp.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() { textComp.getInputMap(JComponent.WHEN_FOCUSED).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released"); textComp.getActionMap().put("Down released", new AbstractAction() { @Override public void actionPerformed(ActionEvent ae) {//focuses the first label on popwindow for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) { if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) { ((SuggestionLabel) suggestionsPanel.getComponent(i)).setFocused(true); autoSuggestionPopUpWindow.toFront(); autoSuggestionPopUpWindow.requestFocusInWindow(); suggestionsPanel.requestFocusInWindow(); suggestionsPanel.getComponent(i).requestFocusInWindow(); break; } } } }); suggestionsPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "Down released"); suggestionsPanel.getActionMap().put("Down released", new AbstractAction() { int lastFocusableIndex = 0; @Override public void actionPerformed(ActionEvent ae) {//allows scrolling of labels in pop window (I know very hacky for now :)) ArrayList sls = getAddedSuggestionLabels(); int max = sls.size(); if (max > 1) {//more than 1 suggestion for (int i = 0; i < max; i++) { SuggestionLabel sl = sls.get(i); if (sl.isFocused()) { if (lastFocusableIndex == max - 1) { lastFocusableIndex = 0; sl.setFocused(false); autoSuggestionPopUpWindow.setVisible(false); setFocusToTextField(); checkForAndShowSuggestions();//fire method as if document listener change occured and fired it } else { sl.setFocused(false); lastFocusableIndex = i; } } else if (lastFocusableIndex <= i) { if (i < max) { sl.setFocused(true); autoSuggestionPopUpWindow.toFront(); autoSuggestionPopUpWindow.requestFocusInWindow(); suggestionsPanel.requestFocusInWindow(); suggestionsPanel.getComponent(i).requestFocusInWindow(); lastFocusableIndex = i; break; } } } } else {//only a single suggestion was given autoSuggestionPopUpWindow.setVisible(false); setFocusToTextField(); checkForAndShowSuggestions();//fire method as if document listener change occured and fired it } } }); } private void setFocusToTextField() { container.toFront(); container.requestFocusInWindow(); textComp.requestFocusInWindow(); } public ArrayList getAddedSuggestionLabels() { ArrayList sls = new ArrayList<>(); for (int i = 0; i < suggestionsPanel.getComponentCount(); i++) { if (suggestionsPanel.getComponent(i) instanceof SuggestionLabel) { SuggestionLabel sl = (SuggestionLabel) suggestionsPanel.getComponent(i); sls.add(sl); } } return sls; } private void checkForAndShowSuggestions() { typedWord = getCurrentlyTypedWord(); suggestionsPanel.removeAll();//remove previos words/jlabels that were added //used to calcualte size of JWindow as new Jlabels are added tW = 0; tH = 0; boolean added = wordTyped(typedWord); if (!added) { if (autoSuggestionPopUpWindow.isVisible()) { autoSuggestionPopUpWindow.setVisible(false); } } else { showPopUpWindow(); setFocusToTextField(); } } protected void addWordToSuggestions(String word) { SuggestionLabel suggestionLabel = new SuggestionLabel(word, suggestionFocusedColor, suggestionsTextColor, this); calculatePopUpWindowSize(suggestionLabel); suggestionsPanel.add(suggestionLabel); } public String getCurrentlyTypedWord() {//get newest word after last white spaceif any or the first word if no white spaces String text = textComp.getText(); String wordBeingTyped = ""; text = text.replaceAll("(\\r|\\n)", " "); 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(textComp.getWidth(), 30)); autoSuggestionPopUpWindow.setSize(tW, tH); autoSuggestionPopUpWindow.setVisible(true); int windowX = 0; int windowY = 0; if (textComp instanceof JTextField) {//calculate x and y for JWindow at bottom of JTextField windowX = container.getX() + textComp.getX() + 5; if (suggestionsPanel.getHeight() > autoSuggestionPopUpWindow.getMinimumSize().height) { windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getMinimumSize().height; } else { windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getHeight(); } } else {//calculate x and y for JWindow on any JTextComponent using the carets position Rectangle rect = null; try { rect = textComp.getUI().modelToView(textComp, textComp.getCaret().getDot());//get carets position } catch (BadLocationException ex) { ex.printStackTrace(); } windowX = (int) (rect.getX() + 15); windowY = (int) (rect.getY() + (rect.getHeight() * 3)); } //show the pop up autoSuggestionPopUpWindow.setLocation(windowX, windowY); autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textComp.getWidth(), 30)); autoSuggestionPopUpWindow.revalidate(); autoSuggestionPopUpWindow.repaint(); } public void setDictionary(ArrayList words) { dictionary.clear(); if (words == null) { return;//so we can call constructor with null value for dictionary without exception thrown } for (String word : words) { dictionary.add(word); } } public JWindow getAutoSuggestionPopUpWindow() { return autoSuggestionPopUpWindow; } public Window getContainer() { return container; } public JTextComponent getTextField() { return textComp; } 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 JTextComponent textComponent; 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.textComponent = 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 = textComponent.getText(); String typedWord = autoSuggestor.getCurrentlyTypedWord(); String t = text.substring(0, text.lastIndexOf(typedWord)); String tmp = t + text.substring(text.lastIndexOf(typedWord)).replace(typedWord, suggestedWord); textComponent.setText(tmp + " "); } } 

正如您所看到的,我通过使构造函数接受JTextComponent而不是JTextFieldJTextArea等来更改代码。

我们留下的问题是我们必须根据传递的JTextComponent显示弹出的JWindow在不同的位置,即JTextField将在底部弹出自动提示窗口,而JTextArea / JEditorPane等将在插入符号下弹出JWindow /字。

AutoSuggestor类中查看这个特定方法showPopUpWindow()

 private void showPopUpWindow() { autoSuggestionPopUpWindow.getContentPane().add(suggestionsPanel); autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textComp.getWidth(), 30)); autoSuggestionPopUpWindow.setSize(tW, tH); autoSuggestionPopUpWindow.setVisible(true); int windowX = 0; int windowY = 0; if (textComp instanceof JTextField) {//calculate x and y for JWindow at bottom of JTextField windowX = container.getX() + textComp.getX() + 5; if (suggestionsPanel.getHeight() > autoSuggestionPopUpWindow.getMinimumSize().height) { windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getMinimumSize().height; } else { windowY = container.getY() + textComp.getY() + textComp.getHeight() + autoSuggestionPopUpWindow.getHeight(); } } else {//calculate x and y for JWindow on any JTextComponent using the carets position Rectangle rect = null; try { rect = textComp.getUI().modelToView(textComp, textComp.getCaret().getDot());//get carets position } catch (BadLocationException ex) { ex.printStackTrace(); } windowX = (int) (rect.getX() + 15); windowY = (int) (rect.getY() + (rect.getHeight() * 3)); } //show the pop up autoSuggestionPopUpWindow.setLocation(windowX, windowY); autoSuggestionPopUpWindow.setMinimumSize(new Dimension(textComp.getWidth(), 30)); autoSuggestionPopUpWindow.revalidate(); autoSuggestionPopUpWindow.repaint(); } 

正如您所看到的那样,我们检查一下JTextComponent是什么实例,如果它不是JTextField只是获取JTextComponent的插入位置(通过插入符号的Rectangle )并从那里弹出位置JWindow (在我的情况下在插入符号下面) 。

我可以提出自己的实现。 它基于JWindow中显示的JList。 由于我想将此代码用于combobox,因此我使用keyEvent.consume()调用禁用了UP和DOWN键。

 import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.BadLocationException; import javax.swing.text.JTextComponent; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class GAutoCompletionDecorator { private final JTextComponent textComponent; private final JWindow suggestionsPopup; private final JList completionList; private final DefaultListModel completionListModel; public static void decorate(JComboBox comboBox, JFrame parent) { comboBox.setEditable(true); final JTextComponent textComponent = (JTextComponent) comboBox.getEditor().getEditorComponent(); decorate(textComponent, parent); } private static void decorate(JTextComponent textComponent, JFrame parent) { final GAutoCompletionDecorator autoCompletionDecorator = new GAutoCompletionDecorator(textComponent, parent); autoCompletionDecorator.decorate(); } private GAutoCompletionDecorator(final JTextComponent textComponent, JFrame parent) { this.textComponent = textComponent; this.suggestionsPopup = new JWindow(parent); this.completionListModel = new DefaultListModel(); this.completionList = new JList(); this.completionList.setModel(completionListModel); this.suggestionsPopup.getContentPane().add(this.completionList); } private void decorate() { textComponent.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent documentEvent) { updateSuggestions(); } public void removeUpdate(DocumentEvent documentEvent) { updateSuggestions(); } public void changedUpdate(DocumentEvent documentEvent) { updateSuggestions(); } }); this.textComponent.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent keyEvent) { if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN || keyEvent.getKeyCode() == KeyEvent.VK_UP) { updateSuggestions(); completionList.requestFocus(); completionList.dispatchEvent(keyEvent); keyEvent.consume(); } } public void keyReleased(KeyEvent keyEvent) { if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN || keyEvent.getKeyCode() == KeyEvent.VK_UP) { keyEvent.consume(); } } }); this.completionList.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent keyEvent) { if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE) { textComponent.requestFocus(); hideSuggestionsPopup(); } else if (keyEvent.getKeyCode() == KeyEvent.VK_UP) { if (completionList.getSelectedIndex() == 0) { completionList.setSelectedIndex(completionListModel.size() - 1); keyEvent.consume(); } } else if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN) { if (completionList.getSelectedIndex() == completionListModel.size() - 1) { completionList.setSelectedIndex(0); keyEvent.consume(); } } else if (keyEvent.getKeyCode() == KeyEvent.VK_BACK_SPACE) { textComponent.requestFocus(); hideSuggestionsPopup(); textComponent.dispatchEvent(keyEvent); } else if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER) { textComponent.requestFocus(); hideSuggestionsPopup(); final String selectedSuggestion = (String) completionList.getSelectedValue(); if (selectedSuggestion != null) { try { final int caretPosition = textComponent.getCaretPosition(); textComponent.getDocument().insertString(caretPosition, getCompletionString(selectedSuggestion, textComponent.getText(), caretPosition), null); } catch (BadLocationException e) { //ignore } } } } }); } private String getCompletionString(String selectedSuggestion, String text, int caretPosition) { //we may insert selectedSuggestion fully of some part of it return selectedSuggestion; } private void updateSuggestions() { final String text = textComponent.getText(); final int caretPosition = textComponent.getCaretPosition(); final List suggestions = getSuggestions(text, caretPosition); if (suggestions == null || suggestions.size() == 0) { //hide suggestions window hideSuggestionsPopup(); } else { //show suggestions window showSuggestionsPopup(suggestions); } } private void hideSuggestionsPopup() { suggestionsPopup.setVisible(false); } private void showSuggestionsPopup(List suggestions) { completionListModel.clear(); for (String suggestion : suggestions) { completionListModel.addElement(suggestion); } final Point textComponentLocation = new Point(textComponent.getLocation()); SwingUtilities.convertPointToScreen(textComponentLocation, textComponent); Point caretLocation = textComponent.getCaret().getMagicCaretPosition(); if (caretLocation != null) { caretLocation = new Point(caretLocation); SwingUtilities.convertPointToScreen(caretLocation, textComponent); } suggestionsPopup.pack(); suggestionsPopup.setLocation(caretLocation == null ? textComponentLocation.x : caretLocation.x, textComponentLocation.y + textComponent.getHeight()); suggestionsPopup.setVisible(true); } private List getSuggestions(String text, int caretPosition) { final List words = new ArrayList(); words.add("suggestion 1"); words.add("suggestion 2"); words.add("suggestion 3"); words.add("suggestion 4"); words.add("suggestion 5"); //make suggestions funny return text.length() < words.size() ? words.subList(0, words.size() - text.length()) : Collections.emptyList(); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { final JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); final JComboBox comboBox = new JComboBox(new String[] {"Choice1", "Choice2"}); comboBox.setEditable(true); GAutoCompletionDecorator.decorate(comboBox, frame); frame.add(comboBox); frame.pack(); frame.setVisible(true); } }); } }