【问题标题】:JTextField take twice the input, but why?JTextField 需要两倍的输入,但是为什么呢?
【发布时间】:2019-02-07 04:22:42
【问题描述】:

我想做一个可编程计算器,我有基本的 GUI,现在我正在尝试设置按钮和显示。我的显示文本基本上是“0”,如果用户输入一个数字,则应该显示该数字。我试图用 KeyListener 来做,但如果我按下一个键,它会显示该键两次。为什么?

 textField.addKeyListener(new KeyListener(){
        boolean newNumber = true;

        public void keyTyped(KeyEvent e) {
        }

        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();


            if(keyCode == e.VK_BACK_SPACE && textField.getText().length() == 1){
                textField.setText("0");
                newNumber = true;
                }

            if(textField.getText().equals("0") && newNumber){
                textField.setText(KeyEvent.getKeyText(keyCode));
                newNumber = false;
            }
        }

        public void keyReleased(KeyEvent e) {
        }

    });

输入前:

“1”后输入:

【问题讨论】:

  • 不要为此使用KeyListener,而是使用DocumentFilter,这是它的设计目的
  • 为了理解“为什么”会发生这种情况,您需要了解文本编辑的“如何”工作。基本上正在发生的事情是,keyPressed 事件已传递给您的侦听器,您正在将文本设置为您想要的值。然后将关键事件传递给JTextField 的侦听器,该侦听器正在使用来自关键事件的信息更新底层Document(已包含1),因此它出现两次 - 这是众多事件之一您不想使用KeyListener 的原因以及任何建议您继续使用“变通”的方法都是一种黑客攻击
  • @MadProgrammer 我猜类似的东西,只是我不知道如何解决它。我已经在这个程序中使用了 DocumentFilter,但老实说,我真的不明白它是如何工作的,所以我无法自己弄清楚。我想我会容易得多,也许不是那么好,但现在已经足够了。
  • 另见calculator example。它使用ScriptEngine 来评估文本字段中的表达式。

标签: java swing jtextfield keylistener


【解决方案1】:

为此,我像这样导出 PlainDocument:

import java.awt.EventQueue;
import java.util.regex.Pattern;

import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

public class DigitDocument extends PlainDocument {

    private static final long       serialVersionUID    = 1L;
    protected static final Pattern  patternStartZero    = Pattern.compile("^0.+");

    protected final JTextField      textField;

    private final int               limit;
    private final Runnable          runnableFormat;

    public DigitDocument(JTextField textField, int limit) {

        super();

        this.textField = textField;
        this.limit = limit;

        runnableFormat = new Runnable() {

            @Override
            public void run() {

                String text = textField.getText();

                if (text.length() == 0) {
                    textField.setText("0");
                }
                else if (patternStartZero.matcher(text).matches()) {
                    textField.setText(text.replaceAll("^0+", ""));
                }
            }
        };
    }

    @Override
    public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {

        str = str.replaceAll("[^0-9]", "");

        if (str.length() == 0)
            return;

        else if ((getLength() + str.length()) <= limit)
            super.insertString(offset, str, attr);

        EventQueue.invokeLater(runnableFormat);
    }

    @Override
    public void remove(int offs, int len) throws BadLocationException {
        if (!"0".equals(textField.getText()))
            super.remove(offs, len);

        EventQueue.invokeLater(runnableFormat);
    }
}

用法是:

textField.setDocument(new DigitDocument(textField, 10));
textField.setText("0");

在数字文档中,

  • 第一个参数是 JTextField 本身。
  • 第二个参数 (10) 是最大输入长度, 您只能输入数字。

【讨论】:

  • 如已建议的那样使用 DocumentFilter。 DocumenFilter 旨在让您无需创建自定义文档。相反,您创建过滤器并将其添加到文档中。不需要所有同步代码。 Swing 事件已在事件调度线程 (EDT) 上执行。
  • 感谢您的编辑和评论。 invokeLater 仍然是必需的,没有它就无法工作。
  • 它不能解决我的问题。正如我所提到的,我已经有一个文档过滤器,但是我真的不明白它是如何工作的,但我想它是一样的,但不能解决我的问题。
【解决方案2】:

这里有一个简单的解决方案:

如果你使用keyPressed,你必须在keyReleased做一些事情,这变成 复杂的。 keyTyped 是一种更简单的方式。

您可以使用e.consume() 来防止插入两位数。

    textField.addKeyListener(new KeyListener() {

        int codeDelete = KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_DELETE);
        int codeBackSpace = KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_BACK_SPACE);

        @Override
        public void keyTyped(KeyEvent e) {

            char keyChar = e.getKeyChar();

            if (textField.getText().length() == 0) {
                textField.setText("0");
            }
            else if (textField.getText().equals("0") && keyChar != codeDelete && keyChar != codeBackSpace) {
                textField.setText(String.valueOf(e.getKeyChar()));
                e.consume();
            }
        }

        @Override
        public void keyPressed(KeyEvent e) {
        }

        @Override
        public void keyReleased(KeyEvent e) {
        }

    });

【讨论】:

  • 您可以使用KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_DELETE) 和KeyEvent.getExtendedKeyCodeForChar(KeyEvent.VK_BACK_SPACE) 来避免这些神奇的值。
  • 是的!这正是我正在寻找的 :-) 我将编辑我的帖子。谢谢。
猜你喜欢
  • 2020-06-26
  • 1970-01-01
  • 2019-08-26
  • 1970-01-01
  • 1970-01-01
  • 2021-12-27
  • 1970-01-01
  • 2010-11-05
  • 1970-01-01
相关资源
最近更新 更多