Implementing auto complete in Java - am I doing it right? [duplicate]

≯℡__Kan透↙ 提交于 2019-12-12 08:25:53

问题


Algorithm

  1. Start
  2. Input a city name - partial or complete
  3. If the user hits enter , take the text from JTextField
  4. Begin brute force search.
  5. If the matches are found, put them in a Vector and put it in a JList
  6. If no match is found, add a String "No Match Found" in Vector
  7. Display JWindow to user containing the results
  8. Stop

Code:

package test;
import javax.swing.*;

import java.awt.Dimension;
import java.awt.event.*;
import java.util.Vector;

public class AutoCompleteTest extends JFrame{
    JTextField city = new JTextField(10);
    String enteredName = null;
    String[] cities = {"new jersey","new hampshire",
            "sussex","essex","london","delhi","new york"};
    JList list = new JList();
    JScrollPane pane = new JScrollPane();
    ResultWindow r = new ResultWindow();
//------------------------------------------------------------------------------
    public static void main(String[] args) {
        new AutoCompleteTest();
    }
//------------------------------------------------------------------------------
    public AutoCompleteTest(){
        setLayout(new java.awt.FlowLayout());
        setVisible(true);
        add(city);
//      add(pane);
        pack();
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        city.addKeyListener(new TextHandler());
    }
//------------------------------------------------------------------------------
    public void initiateSearch(String lookFor){
        Vector<String> matches = new Vector<>();
        lookFor = lookFor.toLowerCase();
        for(String each : cities){
            if(each.contains(lookFor)){
                matches.add(each);
                System.out.println("Match: " + each);
            }
        }
        this.repaint();

        if(matches.size()!=0){
            list.setListData(matches);
            r.searchResult = list;
            r.pane = pane;
            r.initiateDisplay();
        }else{
            matches.add("No Match Found");
            list.setListData(matches);
            r.searchResult = list;
            r.pane = pane;
            r.initiateDisplay();
        }

    }
//------------------------------------------------------------------------------
    public class ResultWindow extends JWindow{
        public JScrollPane pane;
        public JList searchResult;
//------------------------------------------------------------------------------
        public ResultWindow(){

        }
//------------------------------------------------------------------------------
        public void initiateDisplay(){
            pane.setViewportView(searchResult);
            add(pane);
            pack();
            this.setLocation(AutoCompleteTest.this.getX() + 2, 
                    AutoCompleteTest.this.getY()+
                    AutoCompleteTest.this.getHeight());

//          this.setPreferredSize(city.getPreferredSize());
            this.setVisible(true);
        }
    }
//------------------------------------------------------------------------------

    class TextHandler implements KeyListener{
        @Override
        public void keyTyped(KeyEvent e){

        }

        @Override
        public void keyPressed(KeyEvent e){
            if(r.isVisible()){
                r.setVisible(false);
            }
            if(e.getKeyChar() == '\n'){
                initiateSearch(city.getText());
            }
        }

        @Override
        public void keyReleased(KeyEvent e){

        }
    }
//------------------------------------------------------------------------------
}

Output

Problem

The size of the JWindow displaying the results (which is a JList in a JScrollPane) changes based on the results - if the city name is small, JWindow is small, if the city name is big, JWindow is big.

I have tried every possible combination. I tried using setPreferredDimension() of the JWindow, the JList and JScrollPane but the issue won't go.
I want it to match the size of the decorated JFrame no matter what


回答1:


  • JList or JComboBox doesn't returns proper PreferredSize, have to set this value, use JList.setPrototypeCellValue() with pack() for JWindow (must be packed after any changes) and or with JList.setVisibleRowCount(), then value returns getPreferredScrollableViewportSize() for JList in JScrollPane

  • don't to use KeyListener, use DocumentListener (chars can be inserted from system clipboard) for JTextComponents

  • don't to reinvent the wheel, use AutoComplete JComboBox / JTextField, you can to redirect / returns result from matches to the popup JWindow / undecorated JDialog(quite the best workaround for popup recycle)

EDIT

Anyways so basically I will have to manually create a list of all the cities that are to be supported right ?? bx @Little Child

  • this idea could be quite easy, you can to put JTable to the JWindow

  • with one Column,

  • without JTableHeader

  • add there RowSorter (see code example in tutorial)

  • then every steps are done :-), nothing else is required there (maybe bonus to change Background of JTextField in the case that RowFilter returns no matches, add setVisible for popup window from DocumentListener (be sure to test for !isVisible))




回答2:


You should use JComboBox, and for the autocompletion, read this article.




回答3:


You need to use the width of the the JFrame every time you initiate the search and use it calculate the width of the list.

Just change the initiateSearch() function like this:

public void initiateSearch(String lookFor){

    //add the following two statements to set the width of the list.
    int newWidth = AutoCompleteTest.this.getSize().width;        
    list.setPreferredSize(new Dimension(newWidth, list.getPreferredSize().height));

        Vector<String> matches = new Vector<String>();
        lookFor = lookFor.toLowerCase();
        for(String each : cities){
            if(each.contains(lookFor)){
                matches.add(each);
                System.out.println("Match: " + each);
            }
        }
        this.repaint();

        if(matches.size()!=0){
            list.setListData(matches);
            r.searchResult = list;
            r.pane = pane;
            r.initiateDisplay();
        }else{
            matches.add("No Match Found");
            list.setListData(matches);
            r.searchResult = list;
            r.pane = pane;
            r.initiateDisplay();
        }

    }

Here is a sample output:

and

PS: Just for better aesthetics try using some layout to make the text field fill the entire width.




回答4:


One solution would be to change initiateDisplay() to this:

public void initiateDisplay()
    {
        this.pane.setViewportView(this.searchResult);
        this.add(this.pane);
        this.pack();
        this.setLocation(AutoCompleteTest.this.getX() + 2, AutoCompleteTest.this.getY()
                + AutoCompleteTest.this.getHeight());

        int padding = 5;
        int height = this.searchResult.getModel().getSize()
                * AutoCompleteTest.this.city.getSize().height;
        int windowWidth = AutoCompleteTest.this.getSize().width;

        this.setSize(windowWidth, height);
        this.setVisible(true);
    }


来源:https://stackoverflow.com/questions/14849176/implementing-auto-complete-in-java-am-i-doing-it-right

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!