【问题标题】:Consume typed key by implements KeyBindings通过实现 KeyBindings 来使用类型化的键
【发布时间】:2012-03-07 22:54:22
【问题描述】:

您能帮我如何使用KeyBindingConsume 一起输入Chars,就像使用KeyListener 演示我的SSCCE 一样

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;

public class Login {

    private static final long serialVersionUID = 1L;
    /* PassWord for unlock*/
    private PswChecker checker = new PswChecker("pass");

    public Login() {
        JTextField firstField = new JTextField(10);
        firstField.addKeyListener(passwordKeyListener);
        JLabel firstLabel = new JLabel("Password is 'pass' ", JLabel.RIGHT);
        firstLabel.setLabelFor(firstField);
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(0, 2, 5, 5));
        p.add(firstLabel);
        p.add(firstField);
        JFrame f = new JFrame("login");
        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        f.setContentPane(p);
        f.setLocationByPlatform(true);
        f.pack();
        f.setVisible(true);
    }
    //
    private KeyListener passwordKeyListener = new KeyListener() {

        private boolean enabled = true;

        @Override
        public void keyTyped(KeyEvent e) {
            if (!enabled) {
                return;
            }
            if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
                boolean b = checker.accept(e.getKeyChar());
                e.consume();
                if (b) {
                    enabled = false;
                    if (e.getComponent() != null) {
                        e.getComponent().removeKeyListener(this);
                    }
                    unlock();
                }
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }

        @Override
        public void keyPressed(KeyEvent e) {
        }
    };

    void unlock() {
        JOptionPane.showMessageDialog(null, "unlocked");
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                Login log = new Login();
            }
        });
    }

    class PswChecker {

        private String password = null;
        private boolean unlocked = false;
        private long lastInputTimestamp = 0L;
        private int index = 0;

        public PswChecker(String password) {
            if (password == null) {
                throw new IllegalArgumentException("Null password");
            }
            if (password.trim().length() == 0) {
                throw new IllegalArgumentException("Empty password");
            }
            this.password = password;
        }

        public boolean accept(char c) {
            if (unlocked) {
                return true;
            }
            long timestamp = System.currentTimeMillis();
            if (timestamp - lastInputTimestamp > 700) {
                index = 0;
            }
            lastInputTimestamp = timestamp;
            if (password.charAt(index) == c) {
                index++;
            } else {
                if (password.charAt(0) == c) {
                    index = 1;
                } else {
                    index = 0;
                }
            }
            unlocked = (index == password.length());
            return unlocked;
        }

        public boolean isUnlocked() {
            return unlocked;
        }

        public boolean isLocked() {
            return !unlocked;
        }

        @Override
        public String toString() {
            return unlocked ? "UNLOCKED" : "LOCKED";
        }

        /*private boolean check(String keystrokes, String password, boolean expectUnLocked) {
        PswChecker checker = new PswChecker(password);
        for (int i = 0; i < keystrokes.length(); i++) {
        checker.accept(keystrokes.charAt(i));
        }
        return checker.isUnlocked();
        }*/
    }
}

【问题讨论】:

  • +1 这只是一个例子,还是应该使用JPasswordField
  • @trashgod 对,我的问题不是关于 JPasswordField,而是关于使用 KeyBindings 中的事件,
  • 如果不是密码,还有什么?你到底想实现什么?
  • 对,这与密码无关,1) 我不想使用(也许我知道如何使用)DocumentListenerDocumentFilter,2) 想听KeyBindings进入JTextComponent 并确定(在这种情况下,此逻辑是否被转换为密码检查器)是一些Chars 类型并覆盖event.consume() 以输出到View3) 现在我只能将KeyListener 用于event.consume()
  • 为什么? (对不起,近乎大喊,这 15 个字母的限制是...... :)

标签: java swing key-bindings keylistener


【解决方案1】:

为了安全,请考虑JPasswordField,图示here。这将允许使用DocumentFilter,讨论过here

附录:即使是更一般的情况,我也会使用DocumentFilter,如下所示。我会使用key bindings 在组件之间共享Action,如keypad example 所示。

附录:为了说明@kleopatra 的评论,我更新了将ESC 绑定到Reset 的代码。实际上,我只会使用尚未绑定到文本字段操作或正常使用所需的键。

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
import javax.swing.text.*;

/** @see https://stackoverflow.com/q/9610386/230513 */
public class Login {

    private static final String PWD = "pass";
    private static final String RESET = "Reset";
    private PlainDocument doc = new PlainDocument();
    private JTextField text = new JTextField(doc, "", 10);

    public Login() {
        doc.setDocumentFilter(new FieldFilter(PWD));
        JLabel label = new JLabel("Password is '" + PWD + "'", JLabel.RIGHT);
        label.setLabelFor(text);
        text.setToolTipText("Press ESC to reset.");
        text.getInputMap().put(
            KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), RESET);
        text.getActionMap().put(RESET, new Reset());
        JPanel p = new JPanel();
        p.setLayout(new GridLayout(0, 2, 5, 5));
        p.add(label);
        p.add(text);
        JFrame f = new JFrame("Login");
        f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        f.setContentPane(p);
        f.setLocationByPlatform(true);
        f.pack();
        f.setVisible(true);
    }

    private static class FieldFilter extends DocumentFilter {

        private String password;
        private boolean unlocked;
        private StringBuilder sb = new StringBuilder();

        public FieldFilter(String password) {
            this.password = password;
        }

        @Override
        public void replace(FilterBypass fb, int offset, int length,
            String text, AttributeSet attrs) throws BadLocationException {
            if (unlocked) {
                super.replace(fb, offset, length, text, attrs);
            } else {
                sb.append(text);
                unlocked = password.equals(sb.toString());
            }
        }

        public void reset() {
            sb.delete(0, sb.length());
            unlocked = false;
        }
    }

    private static class Reset extends AbstractAction {

        @Override
        public void actionPerformed(ActionEvent e) {
            JTextField jtf = (JTextField) e.getSource();
            PlainDocument doc = (PlainDocument) jtf.getDocument();
            try {
                doc.remove(0, doc.getLength());
            } catch (BadLocationException ex) {
                ex.printStackTrace(System.err);
            }
            FieldFilter filter = (FieldFilter) doc.getDocumentFilter();
            filter.reset();
        }
    }

    public static void main(String[] args) {
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                Login log = new Login();
            }
        });
    }
}

【讨论】:

  • 太好了,太好了,谢谢(以前)up_voted +1,请向我道歉,我的目标是了解 KeyBindings 的工作原理:-(
  • @mKorbel 如果 a) 在任何被搜索的 inputMaps 中找到绑定,并且 b) 绑定操作是启用的,则使用底层 keyEvent
  • @kleopatra 这两种方法是 KeyBindings 的基本功能所必需的,您是在谈论将侦听器移动到第一个父级(JPanel 或...)并从 Action 仅将所需的事件分发到(在这种情况下是从键盘输入的字符)JTextField 还是我又错了,
  • @mKorbel 你指的是哪两种方法?无论如何,如果没有你详细说明原因(请在你的问题中),尤其是为什么通常的方法不能做你想做的事,我无能为力;-)
  • @kleopatra:我更新了这个例子来说明你的观点;欢迎评论。
猜你喜欢
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
  • 2021-02-16
  • 2018-11-25
  • 2014-12-19
相关资源
最近更新 更多