2012-05-03 78 views
9

我正试图建立一个javax.swing.JTextFieldjavax.swing.JList自动完成像谷歌自动完成JTextField和箭头键

  • 当写一个字,Google显示几个比赛

    •    ▼   我可以通过选择一些比赛    ▲      ▼   
    • 可以   ◀    ▶   编辑我的输入。
    • 当我按下   输入                 键搜索框中的内容。
    • 当按一个按Esc该框更改为原始输入。

我aplication是关于Bible,我想寻找的时候我学习的话一个特定的词。我已经看到了Java2sAutoTextField,但没有箭头键的这种特殊行为。

+1

你可以扩展JTextField并给它一个JList作为成员,然后从JTextField手动管理按键吗? 此外,下拉列表不会在容器边缘切断吗? Swing组件是轻量级的'n'。 – Tharwen

+0

@mKorbel如果有更好的解决方案,我想知道更多。实际上,我正在寻找类似于Google的行为,不一定与'JList'类似。 –

+0

@Paul Vargas当然,我会看看,我认为这个问题可以非常简单的编码 – mKorbel

回答

9

这需要一个自定义编码组件。绝对是扩展JTextField的类,并且在该类中有一个包含JList的JPopupMenu。您必须将JPopupMenu右侧放置在文本字段下,以使其看起来像1个组件。

你的下一个关键是要筛选您键入。我通常使用Java6 TableRowSorter与一个JTable连接,并在其中预填充数据。你将需要JTextField上的一些更改监听器,并拦截每个键入的键并获取你的数据。

  1. 键按下
  2. 与entires
  3. 设置RowFilter,它与正则表达式的基础上的JTextField项通过检索数据
  4. 过滤请在DB查询(或一些数据存储得到类似的条目)
  5. 填充的JTable
  6. 管理你的行动与主要听众

编辑

我刮起了样品Swing应用程序,以显示我的陈述。这是一个复制/粘贴的例子,应该立即工作(需要JDK 1.6+)。我基本上得到了你想要的东西,并且在我告诉你填写空格的地方放置了评论。例如,Escape键事件被消耗,你可以随心所欲地做任何事情。

方法initTableModel()刚初始化与数据表中的模型。通常你会想用数据库中的数据动态填充表模型或其他东西。很多都可以调整,但这是例子);所以这应该是一个足够好的例子,你可以修改你的目标。任何比这更多,你必须付我$$$ :)

package test.text.googleclone; 

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Rectangle; 
import java.awt.Toolkit; 
import java.awt.Window; 
import java.awt.event.ActionEvent; 
import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.util.regex.PatternSyntaxException; 
import javax.swing.AbstractAction; 
import javax.swing.BorderFactory; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JPopupMenu; 
import javax.swing.JTable; 
import javax.swing.JTextField; 
import javax.swing.KeyStroke; 
import javax.swing.ListSelectionModel; 
import javax.swing.RowFilter; 
import javax.swing.event.DocumentEvent; 
import javax.swing.event.DocumentListener; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableRowSorter; 

public class SearchAutoFillTest { 

private JFrame frame = null; 
private JTextField searchField = null; 
private JPopupMenu popup = null; 

private JTable searchTable = null; 
private TableRowSorter<DefaultTableModel> rowSorter = null; 
private DefaultTableModel searchTableModel = null; 

public SearchAutoFillTest() { 
    searchTableModel = new DefaultTableModel(); 
    initTableModel(); 

    rowSorter = new TableRowSorter<DefaultTableModel>(searchTableModel); 
    searchTable = new JTable(searchTableModel); 
    searchTable.setRowSorter(rowSorter); 
    searchTable.setFillsViewportHeight(true); 
    searchTable.getColumnModel().setColumnSelectionAllowed(false); 
    searchTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); 
    searchTable.getTableHeader().setReorderingAllowed(false); 
    searchTable.setPreferredSize(new Dimension(775, 100)); 
    searchTable.setGridColor(Color.WHITE); 

    searchField = new JTextField(); 
    searchField.getDocument().addDocumentListener(new DocumentListener() { 
     @Override 
     public void changedUpdate(DocumentEvent e) { 
      showPopup(e); 
     } 

     @Override 
     public void insertUpdate(DocumentEvent e) { 
      showPopup(e); 
     } 

     @Override 
     public void removeUpdate(DocumentEvent e) { 
      showPopup(e); 
     } 
    }); 

    searchField.addKeyListener(new KeyListener() { 
     @Override 
     public void keyTyped(KeyEvent e) { 

     } 

     @Override 
     public void keyReleased(KeyEvent e) { 
      int code = e.getKeyCode(); 
      switch(code) 
      { 
       case KeyEvent.VK_UP: 
       { 
        cycleTableSelectionUp(); 
        break; 
       } 

       case KeyEvent.VK_DOWN: 
       { 
        cycleTableSelectionDown(); 
        break; 
       } 

       case KeyEvent.VK_LEFT: 
       { 
        //Do whatever you want here 
        break; 
       } 

       case KeyEvent.VK_RIGHT: 
       { 
        //Do whatever you want here 
        break; 
       } 
      } 
     } 

     @Override 
     public void keyPressed(KeyEvent e) { 

     } 
    }); 

    KeyStroke keyStroke = KeyStroke.getKeyStroke("ESCAPE"); 
    searchField.getInputMap().put(keyStroke, "ESCAPE"); 
    searchField.getActionMap().put("ESCAPE", new AbstractAction() { 
     @Override 
     public void actionPerformed(ActionEvent e) { 
      //Do what you wish here with the escape key. 
     } 
    }); 

    popup = new JPopupMenu(); 
    popup.add(searchTable); 
    popup.setVisible(false); 
    popup.setBorder(BorderFactory.createEmptyBorder()); 

    JPanel searchPanel = new JPanel(new BorderLayout(5, 5)); 
    searchPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4)); 
    searchPanel.add(searchField, BorderLayout.CENTER); 

    frame = new JFrame(); 
    frame.setLayout(new BorderLayout(5, 5)); 
    frame.add(searchPanel, BorderLayout.NORTH); 
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    frame.setSize(800, 500); 
    center(frame); 
    frame.setVisible(true); 
} 

private final void newFilter() { 
    RowFilter<DefaultTableModel, Object> rf = null; 

    try { 
     rf = RowFilter.regexFilter(getFilterText(), 0); 
    } 
    catch(PatternSyntaxException e) { 
     return; 
    } 

    rowSorter.setRowFilter(rf); 
} 

private final String getFilterText() { 
    String orig = searchField.getText(); 
    return "("+orig.toLowerCase()+")|("+orig.toUpperCase()+")"; 
} 

private void showPopup(DocumentEvent e) { 
    if(e.getDocument().getLength() > 0) { 
     if(!popup.isVisible()) { 
      Rectangle r = searchField.getBounds(); 
      popup.show(searchField, (r.x-4), (r.y+16)); 
      popup.setVisible(true); 
     } 

     newFilter(); 
     searchField.grabFocus(); 

    } 
    else { 
     popup.setVisible(false); 
    } 
} 

private void cycleTableSelectionUp() { 
    ListSelectionModel selModel = searchTable.getSelectionModel(); 
    int index0 = selModel.getMinSelectionIndex(); 
    if(index0 > 0) { 
     selModel.setSelectionInterval(index0-1, index0-1); 
    } 
} 

private void cycleTableSelectionDown() { 
    ListSelectionModel selModel = searchTable.getSelectionModel(); 
    int index0 = selModel.getMinSelectionIndex(); 
    if(index0 == -1) { 
     selModel.setSelectionInterval(0, 0); 
    } 
    else if(index0 > -1) { 
     selModel.setSelectionInterval(index0+1, index0+1); 
    } 
} 

private void initTableModel() { 
    String[] columns = new String[] {"A"}; 
    String[][] data = new String[][] 
    { 
     new String[] {"a"}, 
     new String[] {"aa"}, 
     new String[] {"aaab"}, 
     new String[] {"aaabb"}, 
     new String[] {"aaabbbz"}, 
     new String[] {"b"}, 
     new String[] {"bb"}, 
     new String[] {"bbb"}, 
     new String[] {"bbbbbbb"}, 
     new String[] {"bbbbbbbeee"}, 
     new String[] {"bbbbbbbeeexxx"}, 
     new String[] {"ccc"}, 
     new String[] {"cccc"}, 
     new String[] {"ccccc"}, 
     new String[] {"cccccaaaa"}, 
     new String[] {"ccccccaaaa"}, 
    }; 

    searchTableModel.setDataVector(data, columns); 
} 

private void center(Window w) { 
    int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width; 
    int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height; 

    int windowWidth = w.getWidth(); 
    int windowHeight = w.getHeight(); 

    if (windowHeight > screenHeight) { 
     return; 
    } 

    if (windowWidth > screenWidth) { 
     return; 
    } 

    int x = (screenWidth - windowWidth)/2; 
    int y = (screenHeight - windowHeight)/2; 

    w.setLocation(x, y); 
} 

public static void main(String ... args) { 
    new SearchAutoFillTest(); 
} 
} 
+0

我觉得它很有趣。我会尽力向您发送反馈意见。 –

+0

它确实是一个利他主义软件(我不打算向我的朋友收费),所以从某种意义上说,您为这个事业做出了贡献。 –

+0

酷男,这真是太棒了..希望我可以多给+1。非常好,谢谢分享.. – Anubis

0

默认行为是所有关键事件都转到具有焦点的组件。因此,您需要做的是确定应该真正转到其他组件并将KeyListener安装到这两个组件的钥匙。

在该侦听器中,您可以将事件转发给其他组件。

请参阅this answer如何将事件分派给新组件。在你的情况下,source必须是另一个组件(如果你的文本字段最初接收到事件,则反之亦然)。

1

这听起来像你想有一个JComboBox(见Swing guide),而不是JTextField/JList

当然,那么你有一个下拉按钮,但有可能的方法来处理这个 - 见here

1

这将是沿着这些路线的东西:

import java.awt.event.KeyEvent; 
import java.awt.event.KeyListener; 
import java.util.ArrayList; 

import javax.swing.*; 


public class Component extends JComponent { 
    private final static String[] terms = {"Jesus", 
     "Jesus walks on water" //... 
    }; 
    private static ArrayList<String> recent = new ArrayList<String>(); 

    JTextField jtf; 
    JList jl; 
    public Component(){ 
     // set up design 
     jtf = new JTextField(); 
     jtf.setSize(this.getWidth() - 25, 25); 
     this.add(jtf); 
     //... 
     // add key listeners 
    } 
    class Listener implements KeyListener{ 

     @Override 
     public void keyPressed(KeyEvent arg0) { 

     } 

     @Override 
     public void keyReleased(KeyEvent arg0) { 

     } 

     @Override 
     public void keyTyped(KeyEvent arg0) { 
      if (arg0.getKeyCode() == KeyEvent.VK_DOWN){ 
       // set next item on list 
      } 
      else if (arg0.getKeyCode() == KeyEvent.VK_UP){ 
       // set previous item on list 
      } 
      else if (arg0.getKeyCode() == KeyEvent.VK_ENTER){ 
       // search 
      } 
      else if (arg0.getKeyCode() == KeyEvent.VK_ESCAPE){ 
       jtf.setText(""); 
      } 
            else{ 
             // check list for matches 
            } 
     } 

    } 
} 
3
  • 使用自动完成的JTextField放置到JToolBar中/ MenuBar,注意必须进行排序,使用前ArrayList中,

  • 使用undecoratted的JDialog代替JPopup(仍然有我的一些重要缺陷),

    a)仅创建一个的JDialog与父母JTextField的或的JMenuBar或JFrame中,

    b)中总是要搜索的屏幕上从自动完成的JTextField的getBounds可见的JDialog之前,此范围是用于在屏幕

    c)中包裹上正确possitioning的JDialog的JDialog#调用setVisible(真)到的invokeLater()

  • 覆盖Escape for JDialog.setVisible(false)

  • 放在那里接近/隐藏的JButton到avoiding overrive rest of important methods on focusLost(此日历已经得到外观极好的解决方法上focusLost,点击鼠标,等....,可以说,它是很容易从比较结果来代替日历的funcionality,你要下载的codesource)

  • 你可以放在那里(我认为)6/9 /最大12个按钮,可以删除JButton的感觉通过的setBackground( Color.white)例如,你不可以,请不要用JDialog和这些JButton来做这件事,你的工作只会是setText(“来自比较器的结果”)

  • 如果你的ArrayList为自动完成JTextField已排序,然后你有两个选择

    a)最简单的重写偏差从AutoComplete funcionality通过添加fils单独的数组为setText()为6/9/max 12 butto如果你使用setBackground(Color.white),那么你不会在意怎么隐藏没有文本的JButtons

    b)另一种方法是创建自己的Comparator用于搜索(相同的AutoComplete功能) 6/9 /最多12场比赛,

  • 从6/9 /最多12个Jbutton将捕获的事件使用putClientPropertyEventHandlerSwing Actions,在那里你只能测试是否文本的isEmpty :-),

  • 也许Swing Actions可能是最好的方式,因为它的事件是可升级的,你可以enabled/disable(如果JButtons t ext isEmpty)默认输出这个动作

相关问题