在 Java 中实现自动完成 - 我做得对吗?

2022-01-24 00:00:00 autocomplete java swing jframe jwindow

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() == '
'){
                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

解决方案

  • 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))

相关文章