Создание текстового поля автозаполнения в Java с выпадающим списком

Я хочу создать текстовое поле автоматического предложения, которое будет запрашивать базу данных при каждом событии выпуска ключа. Эта часть проста, но я хочу дать ей хорошее визуальное представление. Что-то похожее на текстовое поле auto-suggest, которое мы видим на таких сайтах, как поиск в Facebook.

Как сделать такой интерфейс ?

наивной идеей было бы разместить JList чуть ниже текстового поля и установить его видимым с результатами в нем при поиске.

лучше идея или стандартный способ сделать это?

6 ответов


@syb0rg ответ проще, так как он использует стороннюю библиотеку.

однако я использовал альтернативный подход:

он использует пользовательский класс с именем AutoSuggestor принимает JTextField, его Window an ArrayList<String> слов для проверки типизированных слов, цвет фона и цвет текста, а также цвет фокуса предложения, а также Значение непрозрачности. Проходя мимо JTextField ссылка a DocumentListener добавляется, который будет выполнять работу по проверке того, какое слово набирается и следует ли отображать предложения или нет, и если да, то какие предложения отображать. Когда слово набирается DocumentListener огоньwordTyped(String wordTyped) метод с текущим словом набирается или (по крайней мере, сколько когда-либо слово было набрано), в wordTyped(..) слова будут сличаться С в AutoSuggestorклассы словарь который является основным ArrayList of String это можно установить на лету, как показано в приведенном ниже примере:

enter image description here

(теперь вам придется использовать мышь и нажмите слово, которое вы хотите автоматически завершить, или используйте вниз к поперечным предложениям и текст и ENTER выбрать предложение при прохождении через вниз ключ. Я еще не реализовал до пока):

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 + " ");
    }
}

как она стоит единственно возможным необходимых дополнений ИМО:

  • до Key Focus traversablity для элементов во всплывающем окне autosuggestions так что мы можем двигаться вверх.

если есть какие-либо перегибы, дайте мне знать, я посмотрю, что я могу сделать. Но, похоже, работает нормально (touch wood).


очень простой способ сделать это-использовать GlazedList реализация автоматического завершения. Очень легко встать и бежать. Вы можете найти его здесь.

вы можете установить автозаполнение на JComboBox только с одной строкой глазурованного кода, например:

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

и SwingX поддерживает автозаполнение и может быть проще в использовании, чем GlazedList. Все вы пишете с SwingX и AutoCompleteDecorator.decorate(comboBox);


чтобы использовать класс TextAutoCompleter, вам нужно загрузить файл Jar AutoCompleter.jar и добавьте его в папку библиотеки вашего проекта, и вот ссылка для загрузки:http://download1689.mediafire.com/4grrthscpsug/7pwzgefiomu392o/AutoCompleter.jar - Nawin

//в основном классе напишите следующий код

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

}

}


Я хотел автозаполнения для редактора в моей среде сборки AVR, поэтому Я написал реализацию это работает так же, как автоматическое завершение в Eclipse (CTRL-SPACE активация, выпадающий список с полосами прокрутки, клавиши курсора + навигация мышью). Он не имеет внешних зависимостей и является всего лишь одним классом. Он должен работать для всех подклассов JTextComponent ; вы можете найти пример использования в папке src/test.


добавьте эти строки в private 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
            }
        }
    });

рабочий подход, который я использовал в проекте, состоял в том, чтобы поместить JTextField поверх JComboBox и сделать базовое поле со списком открытым при вводе в JTextField с помощью прослушивателя документов. Вероятно, вы хотите, чтобы пользовательская модель combobox позволяла более эффективно изменять элементы, поскольку модель по умолчанию, я думаю, позволяет добавлять только элементы по одному, что может быть хитом производительности. чтобы открыть поле со списком, я думаю, что есть способ, чтобы показать его если вы получите его пользовательский интерфейс. Я столкнулся с парой Жуков с прокрутка, когда я пытался изменить элементы, пока он был открыт, поэтому вам может потребоваться закрыть его, изменить элементы и изменить. Для клавиатуры вы можете поймать нажатия клавиш клавиатуры в JTextField и вызвать JComboBox соответствующим образом.