How to show autocomplete as I type in JTextArea?

前端 未结 1 1727
被撕碎了的回忆
被撕碎了的回忆 2021-01-02 04:36

I need to show suggestions (autocomplete) as the user types in a JTextArea, kind of like cell phone T9.

I don\'t know how to do this in myTextArea

相关标签:
1条回答
  • 2021-01-02 05:21

    Here is a snippet to get yourself inspired. You will probably need to reorganize a bit the code to make it more maintainable, but it should give you the gist.

    Basically, we listen for key events (I don't find it relevant to listen to document events, for example if the user pastes some text, I don't want the suggestion panel to appear), and when the caret has at least 2 characters behind, we make some suggestions, using a popupmenu containing a JList of suggestions (here suggestions are really not meaningful, but it would not be too hard to bind this to a dictionnary). As for the shortcuts you are mentionning, it should not be too hard to do so.

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Point;
    import java.awt.event.KeyEvent;
    import java.awt.event.KeyListener;
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    
    import javax.swing.BorderFactory;
    import javax.swing.JFrame;
    import javax.swing.JList;
    import javax.swing.JPanel;
    import javax.swing.JPopupMenu;
    import javax.swing.JTextArea;
    import javax.swing.ListSelectionModel;
    import javax.swing.SwingUtilities;
    import javax.swing.UIManager;
    import javax.swing.UnsupportedLookAndFeelException;
    import javax.swing.text.BadLocationException;
    
    public class Test {
    
        public class SuggestionPanel {
            private JList list;
            private JPopupMenu popupMenu;
            private String subWord;
            private final int insertionPosition;
    
            public SuggestionPanel(JTextArea textarea, int position, String subWord, Point location) {
                this.insertionPosition = position;
                this.subWord = subWord;
                popupMenu = new JPopupMenu();
                popupMenu.removeAll();
                popupMenu.setOpaque(false);
                popupMenu.setBorder(null);
                popupMenu.add(list = createSuggestionList(position, subWord), BorderLayout.CENTER);
                popupMenu.show(textarea, location.x, textarea.getBaseline(0, 0) + location.y);
            }
    
            public void hide() {
                popupMenu.setVisible(false);
                if (suggestion == this) {
                    suggestion = null;
                }
            }
    
            private JList createSuggestionList(final int position, final String subWord) {
                Object[] data = new Object[10];
                for (int i = 0; i < data.length; i++) {
                    data[i] = subWord + i;
                }
                JList list = new JList(data);
                list.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
                list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                list.setSelectedIndex(0);
                list.addMouseListener(new MouseAdapter() {
                    @Override
                    public void mouseClicked(MouseEvent e) {
                        if (e.getClickCount() == 2) {
                            insertSelection();
                        }
                    }
                });
                return list;
            }
    
            public boolean insertSelection() {
                if (list.getSelectedValue() != null) {
                    try {
                        final String selectedSuggestion = ((String) list.getSelectedValue()).substring(subWord.length());
                        textarea.getDocument().insertString(insertionPosition, selectedSuggestion, null);
                        return true;
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                    hideSuggestion();
                }
                return false;
            }
    
            public void moveUp() {
                int index = Math.min(list.getSelectedIndex() - 1, 0);
                selectIndex(index);
            }
    
            public void moveDown() {
                int index = Math.min(list.getSelectedIndex() + 1, list.getModel().getSize() - 1);
                selectIndex(index);
            }
    
            private void selectIndex(int index) {
                final int position = textarea.getCaretPosition();
                list.setSelectedIndex(index);
                SwingUtilities.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        textarea.setCaretPosition(position);
                    };
                });
            }
        }
    
        private SuggestionPanel suggestion;
        private JTextArea textarea;
    
        protected void showSuggestionLater() {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    showSuggestion();
                }
    
            });
        }
    
        protected void showSuggestion() {
            hideSuggestion();
            final int position = textarea.getCaretPosition();
            Point location;
            try {
                location = textarea.modelToView(position).getLocation();
            } catch (BadLocationException e2) {
                e2.printStackTrace();
                return;
            }
            String text = textarea.getText();
            int start = Math.max(0, position - 1);
            while (start > 0) {
                if (!Character.isWhitespace(text.charAt(start))) {
                    start--;
                } else {
                    start++;
                    break;
                }
            }
            if (start > position) {
                return;
            }
            final String subWord = text.substring(start, position);
            if (subWord.length() < 2) {
                return;
            }
            suggestion = new SuggestionPanel(textarea, position, subWord, location);
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    textarea.requestFocusInWindow();
                }
            });
        }
    
        private void hideSuggestion() {
            if (suggestion != null) {
                suggestion.hide();
            }
        }
    
        protected void initUI() {
            final JFrame frame = new JFrame();
            frame.setTitle("Test frame on two screens");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JPanel panel = new JPanel(new BorderLayout());
            textarea = new JTextArea(24, 80);
            textarea.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
            textarea.addKeyListener(new KeyListener() {
    
                @Override
                public void keyTyped(KeyEvent e) {
                    if (e.getKeyChar() == KeyEvent.VK_ENTER) {
                        if (suggestion != null) {
                            if (suggestion.insertSelection()) {
                                e.consume();
                                final int position = textarea.getCaretPosition();
                                SwingUtilities.invokeLater(new Runnable() {
                                    @Override
                                    public void run() {
                                        try {
                                            textarea.getDocument().remove(position - 1, 1);
                                        } catch (BadLocationException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                });
                            }
                        }
                    }
                }
    
                @Override
                public void keyReleased(KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_DOWN && suggestion != null) {
                        suggestion.moveDown();
                    } else if (e.getKeyCode() == KeyEvent.VK_UP && suggestion != null) {
                        suggestion.moveUp();
                    } else if (Character.isLetterOrDigit(e.getKeyChar())) {
                        showSuggestionLater();
                    } else if (Character.isWhitespace(e.getKeyChar())) {
                        hideSuggestion();
                    }
                }
    
                @Override
                public void keyPressed(KeyEvent e) {
    
                }
            });
            panel.add(textarea, BorderLayout.CENTER);
            frame.add(panel);
            frame.pack();
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (UnsupportedLookAndFeelException e) {
                e.printStackTrace();
            }
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    new Test().initUI();
                }
            });
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题