JFormattedTextField 货币格式化程序在插入数字后输入两个 0
JFormattedTextField currency formatter enters two 0 after inserting number
在这个 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);
每次数字到达分隔符时,它都会添加两个额外的零。
我怎样才能防止这种情况发生?
您正在覆盖小数点分隔符。当包含的文本是 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(…)
以便更轻松地编写更多位数的数字,但删除小数位将变得不直观。
在这个 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);
每次数字到达分隔符时,它都会添加两个额外的零。 我怎样才能防止这种情况发生?
您正在覆盖小数点分隔符。当包含的文本是 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(…)
以便更轻松地编写更多位数的数字,但删除小数位将变得不直观。