【问题标题】:JFormattedTextField currency formatter enters two 0 after inserting numberJFormattedTextField 货币格式化程序插入数字后输入两个 0
【发布时间】:2020-07-05 18:54:56
【问题描述】:

在这个 GIF 中,你可以看到哪里出了问题:

这个 JFormattedTextField 和 JFormatter 的代码如下所示:

    NumberFormat format = NumberFormat.getCurrencyInstance(Locale.GERMANY);
    format.setMaximumFractionDigits(2);
    NumberFormatter formatter = new NumberFormatter(format);
    formatter.setAllowsInvalid(false);
    formatter.setOverwriteMode(true);
    JFormattedTextField price = new JFormattedTextField(formatter);
    price.setValue(0.00);

每当一个数字碰到分隔符时,它就会添加两个额外的零。 我怎样才能防止这种情况发生?

【问题讨论】:

  • 很难从您的代码片段中看出,但您应该在创建 GUI 时定义一次 JFormattedTextField。如果你发布了一个minimal reproducible example,一个人可以复制、粘贴到他的 Eclipse 中并运行,那么你可能会得到一些帮助

标签: java swing currency formatter jformattedtextfield


【解决方案1】:

您正在覆盖小数分隔符。当包含的文本是1,00 €,对应于1.0,并且你用1覆盖,,结果文本首先是1100 €,它将被解析为1100.0,因为NumberFormat是宽容的关于缺少或错位的分组分隔符,以及缺少小数部分。然后将号码1100.0 重新格式化为1.100,00 €

当您继续输入时,这会重复。

逗号被覆盖的事实似乎是一个错误。我在NumberFormatter找到了如下代码:

/**
 * Subclassed to treat the decimal separator, grouping separator,
 * exponent symbol, percent, permille, currency and sign as literals.
 */
boolean isLiteral(Map<?, ?> attrs) {
    if (!super.isLiteral(attrs)) {
        if (attrs == null) {
            return false;
        }
        int size = attrs.size();

        if (attrs.get(NumberFormat.Field.GROUPING_SEPARATOR) != null) {
            size--;
            if (attrs.get(NumberFormat.Field.INTEGER) != null) {
                size--;
            }
        }
        if (attrs.get(NumberFormat.Field.EXPONENT_SYMBOL) != null) {
            size--;
        }
        if (attrs.get(NumberFormat.Field.PERCENT) != null) {
            size--;
        }
        if (attrs.get(NumberFormat.Field.PERMILLE) != null) {
            size--;
        }
        if (attrs.get(NumberFormat.Field.CURRENCY) != null) {
            size--;
        }
        if (attrs.get(NumberFormat.Field.SIGN) != null) {
            size--;
        }
        return size == 0;
    }
    return true;
}

与文档注释的枚举相反,其中提到“小数分隔符”,字段NumberFormat.Field.DECIMAL_SEPARATOR 不在方法内处理。

由于这是一个包私有方法,与大多数实现一样,无法从应用程序中更改此行为。我们可以通过创建自定义 NumberFormat 来在传入的 NumberFormat 端放置一个解决方法,它已经在字符迭代器中删除了 DECIMAL_SEPARATOR 的标记。

首先,我们添加一个辅助方法

/**
 * Converts DECIMAL_SEPARATOR fields to literal text.
 */
static AttributedCharacterIterator
    convertSeparatorFieldToLiteral(AttributedCharacterIterator aci) {

    if(aci.current() == CharacterIterator.DONE) return aci;
    int o = aci.getBeginIndex();
    StringBuilder sb = new StringBuilder(aci.getEndIndex()-o);
    do sb.append(aci.current()); while(aci.next() != CharacterIterator.DONE);
    AttributedString s = new AttributedString(sb.toString());
    for(aci.first(); aci.current() != AttributedCharacterIterator.DONE;
        aci.setIndex(aci.getRunLimit())) {

        Map<AttributedCharacterIterator.Attribute, Object> attr = aci.getAttributes();
        if(attr == null || attr.isEmpty()) continue;
        if(attr.containsKey(NumberFormat.Field.DECIMAL_SEPARATOR)) {
            if(attr.size() == 1) continue;
            attr = new HashMap<>(attr);
            attr.remove(NumberFormat.Field.DECIMAL_SEPARATOR);
        }
        s.addAttributes(attr, aci.getRunStart()-o, aci.getRunLimit()-o);
    }
    return s.getIterator();
}

然后,将示例用法更改为

NumberFormat format = NumberFormat.getCurrencyInstance(Locale.GERMANY);
format.setMaximumFractionDigits(2);
NumberFormat myFormat = new NumberFormat() {
    @Override
    public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition p) {
        return format.format(number, toAppendTo, p);
    }
    @Override
    public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
        return format.format(number, toAppendTo, pos);
    }
    @Override
    public Number parse(String source, ParsePosition parsePosition) {
        return format.parse(source, parsePosition);
    }
    @Override
    public AttributedCharacterIterator formatToCharacterIterator(Object obj) {
        AttributedCharacterIterator aci = format.formatToCharacterIterator(obj);
        return convertSeparatorFieldToLiteral(aci);
    }
};

NumberFormatter formatter = new NumberFormatter(myFormat);

formatter.setAllowsInvalid(false);
formatter.setOverwriteMode(true);
JFormattedTextField price = new JFormattedTextField(formatter);
price.setValue(0.00);

自定义NumberFormat 在几乎所有方面都代表原始格式。唯一的区别是 DECIMAL_SEPARATOR 属性从字符迭代器中移除,因此小数点分隔符将被视为无法更改格式的文字。

因此,您可以在覆盖模式下直接键入。但是,在覆盖模式下处理JFormattedTextField 仍然很棘手。您需要setMinimumIntegerDigits(…) 才能更轻松地编写更多位数的数字,但删除十进制数字会变得不直观。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多