自动完成JTextField和箭头键

时间:2012-05-03 22:55:37

标签: java swing text jtextfield

我正在尝试使用javax.swing.JTextField构建javax.swing.JList以自动完成,例如 Google

  • 撰写单词时,Google会显示多个匹配

    • 时,我可以使用匹配 kbd>和
    • 可以使用编辑我的输入。
    • 当我按 Enter 键时,搜索框中的内容。
    • 当按 Esc 时,框会更改为原始输入。

我的答案是关于Bible,我想在研究这个词时寻找一个特定的词。我见过Java2sAutoTextField,但箭头键没有这种特殊行为。

6 个答案:

答案 0 :(得分:9)

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

您的下一个技巧是在键入时进行过滤。我通常使用Java6 TableRowSorter和一个JTable来做这个,我用它来预先填充数据。您需要在JTextField上使用一些更改侦听器并拦截键入的每个键并获取您的数据。

  1. 按键
  2. 在DB中执行查询(或某些数据存储以获取类似条目)
  3. 使用这些内容填充JTable
  4. 使用基于JTextField条目的正则表达式设置RowFilter以过滤检索到的数据
  5. 使用主要侦听器管理您的操作
  6. 修改

    我掀起了一个示例摇摆应用程序,以显示我说的内容。这是一个复制/粘贴示例,应该可以正常工作(需要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();
    }
    }
    

答案 1 :(得分:3)

此组件称为自动完成,并包含在所谓的swing扩展项中。

请看一下:http://swingx.java.net/
有一个带演示的webstart:http://swinglabs-demos.java.net/demos/swingxset6/swingxset.jnlp

答案 2 :(得分:3)

  • 使用自动完成JTextField放入JToolBar / MenuBar,注意你必须在使用前对ArrayList进行排序,

  • 使用未发现的JDialog而不是JPopup(仍然有一些重要的错误),

    a)只创建一个JDialog,其父级为JTextField或JMenuBar或JFrame,

    b)总是在屏幕上可见的JDialog之前从AutoComplete JTextField中搜索getBounds,这个Bounds用于在屏幕上正确显示JDialog

    c)将JDialog #setVisible(true)包装到invokeLater()

  • 覆盖Escape for JDialog.setVisible(false)

  • 将JButton关闭/隐藏到avoiding overrive rest of important methods on focusLost(这个日历在focusLost,mouseClick等等方面有很好的解决方法,用比较器的结果替换日历功能是否非常容易,你必须下载codesource)

  • 你可以放在那里(我的观点)6/9 /最多12个按钮,你可以通过setBackground(Color.white)删除JButton Feels,例如,你不能,请不要用它来做JDialog和这些JButton,你的工作只会是setText(“比较器的结果”)

  • 如果您的AutoComplete JTextField的ArrayList已经排序,那么您有两个选择

    a)来自AutoComplete功能的最简单的覆盖偏差,通过为弹出式JDialog上的6/9 / max 12按钮添加fils单独的数组for setText(),如果你设置了BackBackground(Color.white),那么你不在乎某种方式隐藏没有文字的JButtons

    b)另一种方法是创建自己的比较器进行搜索(相同的自动完成功能)第6/9 /最多12场比赛,

  • 用于从6/9 /最多12个JButton捕获事件使用putClientPropertyEventHandlerSwing Actions,您只需测试文本是否为空:-),< / p>

  • 也许Swing Actions可能是最好的方法,因为它的事件是可扩展的,默认情况下你可以enabled/disable(如果JButtons发出文本isEmpty)输出

答案 3 :(得分:1)

听起来您需要JComboBox(请参阅Swing guide)而不是JTextField / JList

当然,您有一个下拉按钮,但有可能的方法来解决这个问题 - 请参阅here

答案 4 :(得分: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
                                    }
        }

    }
}

答案 5 :(得分:0)

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

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

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