您正在覆盖小数分隔符。当包含的文本是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(…) 才能更轻松地编写更多位数的数字,但删除十进制数字会变得不直观。