2

如果有人回答,请告诉我,这个答案。基本上像谷歌搜索引擎一样需要,当我们按下任何键时,它将显示与建议相关的按键。

考虑萨蒂什·迪曼

4

2 回答 2

9

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

JTextField与 一起使用AutoSuggestor

在此处输入图像描述

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

        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<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(JTextComponent textComp, Window mainWindow, ArrayList<String> 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<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();
        textComp.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 = 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<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 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 + " ");
    }
}

如您所见,我通过使其构造函数接受 aJTextComponent而不是 aJTextFieldJTextArea等来更改代码。

我们剩下的问题是我们必须JWindow根据JTextComponent传递的内容在不同的位置显示弹出窗口,即 aJTextField将在底部弹出自动建议窗口,而JTextArea/ JEditorPaneetc 将JWindow在插入符号/单词下弹出。

showPopUpWindow()在课堂上看看这个特定的方法AutoSuggestor

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简单地获取插入符号的位置(通过Rectangle插入符号的),JTextComponent并且位置JWindow从那里弹出(在我的例子中位于插入符号的下方)。

于 2013-03-05T09:22:34.413 回答
0

我可以提出我自己的实现。它基于 JWindow 中显示的 JList。由于我想将此代码用于组合框,因此我使用 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<String> 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<String> 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<String> getSuggestions(String text, int caretPosition) {
        final List<String> words = new ArrayList<String>();
        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.<String>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);
            }
        });

    }
}
于 2016-01-08T11:06:09.720 回答