【问题标题】:How to get the same value as the user is seeing from a JFormattedTextField?如何获得与用户从 JFormattedTextField 看到的值相同的值?
【发布时间】:2010-02-05 10:00:04
【问题描述】:

我正在使用NumberFormatterJFormattedTextField,但.getValue() 不会返回与用户看到的相同的值。

我认为输入字符串是使用 NumberFormats 解析方法解析的,我从 NumberFormat.getNumberInstance(); 获得带有实际语言环境的 Numberformat。所以我认为我不能轻易扩展它并编写自己的解析方法?

例如,如果用户输入1234.487 getValue() 将返回: 1234.487 但会显示用户1,234.49

另一个例子,使用NumberFormat.getCurrencyInstance();。用户键入1234.487getValue() 将返回1234.487 但用户将显示$1,234.49

相反,如果 Formatter 无法在不四舍五入的情况下格式化值,我希望生成 ParseException。如果用户键入 4.35b6,默认情况下格式化程序将显示 4.35 并且值将是 4.35,但我想要 ParseException,因为用户键入了 invalid 值。

这是我尝试过的代码:

NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(2);
nf.setMinimumFractionDigits(2);
final JFormattedTextField ftf = new JFormattedTextField(nf);
ftf.setValue(new BigDecimal("1234.50"));

// Print the value from ftf
final JTextField txt = new JTextField(12);
txt.addFocusListener(new FocusAdapter() {
    public void focusGained(FocusEvent e) {
        txt.setText(ftf.getValue().toString());
    }
});

如何获得与用户看到的相同的值?

【问题讨论】:

标签: java swing validation number-formatting jformattedtextfield


【解决方案1】:

您应该扩展 NumberFormatter 而不是 NumberFormat 并覆盖 stringToValue 以便它验证在解析字符串时您会返回原始值:

class StrictNumberFormatter extends javax.swing.text.NumberFormatter {
    @Override
    public Object stringToValue(String text) throws ParseException {
        Object parsedValue = super.stringToValue(text);
        String expectedText = super.valueToString(parsedValue);
        if (! super.stringToValue(expectedText).equals(parsedValue)) {
            throw new ParseException("Rounding occurred", 0);
        }
        return parsedValue;
    }

    public StrictNumberFormatter(NumberFormat nf) {
        super(nf);
    }
}

NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(2);
nf.setMinimumFractionDigits(2);
JFormattedTextField.AbstractFormatter formatter = new StrictNumberFormatter(nf);
final JFormattedTextField ftf = new JFormattedTextField(formatter);
ftf.setValue(new BigDecimal("1234.50"));

如果舍入前后的值不匹配,此格式化程序将拒绝新文本。

注意:这里比较的是,而不是字符串。它将容忍诸如删除分组字符 ("$1,000" => "$1000") 和尾随零 ("$1.20" => "$1.2") 之类的更改。基本上,如果NumberFormat 返回相同的值,那么它是可以接受的。但是NumberFormat 施加的任何限制仍然适用,例如您不得删除货币符号或插入前导空格等。

【讨论】:

  • @Ash,是的,我刚刚用getCurrencyInstance() 尝试过。它应该适用于任何有效的NumberFormat
  • 使用此解决方案编辑文本字段要容易得多,因此我将使用此解决方案。谢谢!
  • 我只是想感谢您提供优雅的解决方案。这个问题把我难住了!
【解决方案2】:

试试这个:

txt.setText(ftf.getFormatter().valueToString(ftf.getValue()));

您还必须处理valueToString 方法抛出的java.text.ParseException

编辑: 可以将格式化程序设置为完全禁止无效条目,这可能会有所帮助:

    ((DefaultFormatter)ftf.getFormatter()).setAllowsInvalid( false );

【讨论】:

  • 那行不通。然后您必须再次解析字符串,因为字符串包含格式,如货币符号和数字分组。而且我认为没有货币符号字符串的解析器?
  • 当我运行您的代码(进行上述更改)并输入“1234.487”时,我在两个字段中都看到“1,234.49”。这不是你想要的吗?
  • 不,我想要的是值,而不是字符串,所以我可以进行计算。如果我想要那样的话,我可以使用 getText()。我认为如果用户键入三个小数位,则应生成 ParseException 并且格式化程序不应进行舍入,这很重要。如果我确实喜欢您的示例,则该值的分组符号介于 1 和 2 之间,因此我必须再次解析它。
  • @Sanoj:好的,所以您希望格式化程序在用户输入无效内容后立即告诉您(通过 ParseException)?
  • 考虑到格式化程序在值和文本之间的区别,这非常棘手。您可以将默认格式化程序设置为完全禁止无效条目 - 这有帮助吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-03
  • 2020-09-24
  • 2023-03-09
  • 2015-04-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多