【问题标题】:Cleaner way to 'sanitize' JTextField input?'清理' JTextField 输入的更清洁方法?
【发布时间】:2012-04-17 18:22:20
【问题描述】:

所以我正在为 JTextField 编写 TextValueChanged 处理程序,该处理程序需要很长时间才能输入。它只需要允许用户输入介于 0 和 Long.MAX 之间的有效值。如果用户键入了一个无效字符,它应该去掉它,以便 JTextField 中的值始终是一个有效的 long。

我当前的代码看起来像这样,但看起来很难看。没有外部库(包括 Apache Commons),是否有更清洁/更简单的方法来做到这一点?

public void textValueChanged(TextEvent e) {
    if (e.getSource() instanceof JTextField) {
        String text = ((JTextField) e.getSource()).getText();
        try {
            // Try to parse cleanly
            long longNum = Long.parseLong(text);
            // Check for < 1 
            if (longNum < 1) throw new NumberFormatException();
            // If we pass, set the value and return
            setOption("FIELDKEY", longNum);
        } catch (NumberFormatException e1) {
            // We failed, so there's either a non-numeric or it's too large.
            String s = ((JTextField) e.getSource()).getText();
            // Strip non-numeric characters
            s = s.replaceAll("[^\\d]", "");
            long longNum = -1;
            if (s.length() != 0) {
                /* Really ugly workaround for the fact that a
                 * TextValueChanged event can capture more than one
                 * keystroke at a time, if it's typed fast enough,
                 * so we might have to strip more than one
                 * character. */
                Exception e3;
                do {
                    e3 = null;
                    try {
                        // Try and parse again
                        longNum = Long.parseLong(s);
                    } catch (NumberFormatException e2) {
                        // We failed, so it's too large.
                        e3 = e2;
                        // Strip the last character and try again.
                        s = s.substring(0, s.length() - 1);
                    }
                    // Repeat
                } while (e3 != null);
            }
            // We parsed, so add it (or blank it if it's < 1) and return.
            setOption("FIELDKEY", (longNum < 1 ? 0 : longNum));
        }
    }
}

该字段不断从 getOption(key) 调用中重新填充,因此要“存储”该值,只需将其传递给该调用即可。

【问题讨论】:

  • 改用JFormattedTextField 有什么问题?
  • 我可以这样做,但它的行为与我想要的不符。如果失去焦点,您要么丢失更改并恢复到以前保存的文本(我不想要),要么保留无效文本(我也不想要)。要强制这种行为,我只需要覆盖它并在不同的事件中编写一些代码。
  • JFormattedTextField 的问题是你可以输入任何你喜欢的东西。只有在您退出文本字段后才会进行验证。

标签: java swing jtextfield formatted-input


【解决方案1】:

DocumentFilter 是一个很好的解决方案。

The Definitive Guide to Java Swing by John Zukowski 在第 542-546 页上有一个很好的示例,展示了用于限制整数范围的自定义文档过滤器。

提供自定义 DocumentFilter 应该可以轻松满足在复制/粘贴时去除不需要的字符的要求,正如您在其中一个 cmets 中指定的那样。

【讨论】:

  • 我通过 DocumentFilter 简单地查看了一下,并认为我看到了一些让我认为它是一个交易破坏者的东西。我对它不是很熟悉,所以完全有可能我弄错了。我再看看,谢谢。
  • 正如 Zukowski 所说:虽然您当然可以创建 Document 的自定义子类,但更面向对象的方法是创建一个过滤器,因为您不想更改 Document;你只是想限制模型的输入。
  • 我怀疑我会在这个例子中使用它,但经过一些研究后,我可能会回去创建一些通用 DocumentFilters 并将它们扔到一个 util 包中。我认为我立即使用它们的开销会太大,但它们会成为一个很好的长期解决方案。感谢您的提醒。
【解决方案2】:

【讨论】:

  • 这和 JFormattedTextField 有同样的问题,它只在获得/失去焦点时起作用,我需要这个字段进行持续验证。
【解决方案3】:

我有这个你可以长期使用的浮动

class DoubleTextDocument extends PlainDocument {
    public void insertString(int offs, String str, AttributeSet a)
            throws BadLocationException {
        if (str == null)
            return;
        String oldString = getText(0, getLength());
        String newString = oldString.substring(0, offs) + str
                + oldString.substring(offs);
        try {
            Float.parseFloat(newString + "0");
            super.insertString(offs, str, a);
        } catch (NumberFormatException e) {
        }
    }
}

您可以在扩展的JTextField 中使用

@Override

protected Document createDefaultModel() {
    return new DoubleTextDocument();
}

【讨论】:

  • Document 实现的主要问题是它不能按照我想要的方式工作。如果添加了多个字符并且只有少数无效,它将吃掉所有字符并恢复到以前的安全值,而不是仅仅清除无效的值。话虽如此,这是我迄今为止想到的下一个最佳选择。我希望有人有一些东西,它本质上是我所拥有的东西的重构和经过深思熟虑的版本。
  • 好的,所以你需要一个场景,如果复制粘贴字母数字文本,只需要考虑数字部分
  • 是的。抱歉,我应该在主帖中说明这是一个约束。
猜你喜欢
  • 2020-03-05
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
  • 1970-01-01
相关资源
最近更新 更多