用Java实现自动完成 – 我做得对吗?

算法

  1. 开始
  2. 输入城市名称 – 部分或完整
  3. 如果用户按Enter键,则从JTextField获取文本
  4. 开始蛮力搜索。
  5. 如果找到匹配项,则将它们放在Vector并将其放入JList
  6. 如果未找到匹配项,请在Vector添加“找不到匹配项” String
  7. 向包含结果的用户显示JWindow
  8. 停止

码:

 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 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){ } } //------------------------------------------------------------------------------ } 

产量

在此处输入图像描述

问题

显示结果的JWindow的大小(它是JScrollPaneJList )根据结果而变化 – 如果城市名称很小, JWindow很小,如果城市名称很大,则JWindow很大。

我尝试了所有可能的组合。 我尝试使用JWindow setPreferredDimension()JListJScrollPane但问题不会出现。
我想让它与装饰的JFrame的大小相匹配,无论如何

  • JListJComboBox不返回正确的PreferredSize ,必须设置此值,对JWindow使用带有pack() JList.setPrototypeCellValue() (必须在任何更改后打包)和/或使用JList.setVisibleRowCount() ,然后返回值getPreferredScrollableViewportSize()用于JScrollPane JList

  • 不要使用KeyListener ,使用DocumentListener (可以从系统剪贴板插入字符)为JTextComponents

  • 不要重新发明轮子,使用AutoComplete JComboBox / JTextField ,你可以将匹配的结果重定向/返回到弹出的JWindow /未修饰的JDialog (弹出回收的最佳解决方法)

编辑

无论如何基本上我将不得不手动创建所有要支持的城市的列表吗? bx @Little Child

  • 这个想法可能很简单,你可以将JTable放到JWindow

  • Column

  • 没有JTableHeader

  • 在那里添加RowSorter (参见教程中的代码示例)

  • 然后每个步骤都完成了:-),那里没有其他任何东西(在RowFilter没有返回匹配的情况下,可能会更改JTextField Background ,从DocumentListener为弹出窗口添加setVisible (一定要测试!isVisible ))

您应该使用JComboBox ,并且对于自动完成,请阅读本文 。

每次启动搜索时都需要使用JFrame的宽度,并使用它来计算列表的宽度。

只需像这样更改initiateSearch()函数:

 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 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(); } } 

这是一个示例输出:

小尺寸

在此处输入图像描述

PS:为了更好的美学,尝试使用一些布局使文本字段填充整个宽度。

一种解决方案是将initiateDisplay()更改为:

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