用于输入字段的 Sencha GXT 自定义货币格式

Sencha GXT Custom CurrencyFormat for input-field

我目前正在开发一个使用 Sencha GXT 库的 GWT 项目。我目前正在尝试创建具有自定义货币格式的 CurrencyField。我想要自定义货币格式的一些示例:

€ 123,45
€ 98.765.432,10
€ 400,00

因此,如您所见,我想要一个前缀欧元符号; a space 介于货币符号和小数点之间;千位分隔符的点;和小数逗号(当然还有逗号后面的两位小数)。

但在 GXT 中似乎无法创建您自己的自定义格式。 我知道使用常规 java.text.NumberFormat 我可以这样做:

NumberFormat format = NumberFormat.getCurrencyInstance();
DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols();
formatSymbols.setCurrencySymbol("€");
formatSymbols.setGroupingSeparator('.');
formatSymbols.setMonetaryDecimalSeparator(',');
((DecimalFormat)format).setDecimalFormatSymbols(formatSymbols);

但是在 GXT 中,com.google.gwt.i18n.client.NumberFormat 必须用于 NumberField<BigDecimal>setFormat 方法。我知道一些自定义可以像这样与 CurrencyData 一起使用:

private CurrencyData createCurrencyData() {
  // CurrencyData docs: http://www.gwtproject.org/javadoc/latest/com/google/gwt/i18n/client/CurrencyData.html
  return new CurrencyData() {

    @Override
    public String getCurrencySymbol() {
      return "€";
    }

    @Override
    public String getSimpleCurrencySymbol() {
      return "€";
    }

    @Override
    public String getPortableCurrencySymbol() {
      return "€";
    }

    @Override
    public String getCurrencyCode() {
      return "EUR"; // ISO4217 for this currency
    }

    @Override
    public int getDefaultFractionDigits() {
      return 2; // Amount of decimal positions
    }

    @Override
    public boolean isSymbolPrefix() {
      return true; // true to place currency symbol before the decimal
    }

    @Override
    public boolean isSymbolPositionFixed() {
      return true; // true to use the same currency symbol position regardless of locale (determined by the isSymbolPrefix-method)
    }

    @Override
    public boolean isSpacingFixed() {
      return true; // true to put a space between the currency symbol and the decimal
    }

    @Override
    public boolean isSpaceForced() {
      return true; // true to use the same spacing regardless of locale (determined by the isSpacingFixed-method)
    }

    @Override
    public boolean isDeprecated() {
      return false;
    }
  };
}

我现在是这样使用的:

NumberField<BigDecimal> currencyField = new NumberField<BigDecimal>(
  new NumberPropertyEditor.BigDecimalPropertyEditor());
currencyField.setFormat(NumberFormat.getCurrencyFormat(createCurrencyData()));

但是有两个问题:

  1. 它没有完全按照我想要的方式定制。它现在接受这样的输入:€123,456.78;而不是 € 123.456,78(另外,即使 CurrencyData#isSpacingForced 显然应该用于货币符号和小数之间的 space,它在 NumberField 上不起作用。 .)
  2. 当我只是输入一个像 400 这样的常规数字时,它会给出一个错误而不是自动格式化用户输入:400 does not have either positive or negative affixes is not a valid number.

我们所做的更改:

我们现在将荷兰语区域设置放入 .gwt.xml 文件中:

<extend-property name="locale" values="nl_NL"/>
<set-property name="locale" value="nl_NL"/>
<set-property-fallback name="locale" value="nl_NL"/>

我们使用这样的自定义格式:

final BigDecimalField currencyField = new BigDecimalField();
currencyField.setFormat(CustomNumberFormat.getCurrencyFormat());

使用 CustomNumberFormat-class 像这样:

import java.util.Map;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.CurrencyData;
import com.google.gwt.i18n.client.CurrencyList;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.i18n.client.constants.CurrencyCodeMapConstants;

public class CustomNumberFormat extends NumberFormat {

  private static CurrencyCodeMapConstants currencyCodeMapConstants = GWT.create(CurrencyCodeMapConstants.class);
  private static char currencySymbol;
  private static NumberFormat cachedCurrencyFormat;

  /**
   * Get the default currency format
   * @return the default currency format
   */
  public static NumberFormat getCurrencyFormat() {
    if (cachedCurrencyFormat == null) {
      cachedCurrencyFormat = getCurrencyFormat(CurrencyList.get().getDefault().getCurrencyCode());
    }
    return cachedCurrencyFormat;
  }

  /**
   * Get the currency format
   * @param currencyCode the code to use
   * @return the {@link NumberFormat} to use
   */
  public static NumberFormat getCurrencyFormat(final String currencyCode) {
    return new CustomNumberFormat(defaultNumberConstants.currencyPattern(), lookupCurrency(currencyCode), false, true);
  }

  /**
   * Lookup the currency data
   * @param currencyCode the currency code e.g. EUR
   * @return the {@link CurrencyData}
   */
  private static CurrencyData lookupCurrency(final String currencyCode) {
    final CurrencyData currencyData = CurrencyList.get().lookup(currencyCode);

    final Map<String, String> currencyMap = currencyCodeMapConstants.currencyMap();

    final String code = currencyData.getCurrencyCode();
    final String symbol = currencyMap.get(currencyCode);
    final int fractionDigits = currencyData.getDefaultFractionDigits();
    final String portableSymbol = currencyData.getPortableCurrencySymbol();
    return toCurrencyData(code, symbol, fractionDigits, portableSymbol);
  }

  /**
   *
   * @param code the currency code e.g. EUR
   * @param symbol the currency symbol e.g. the euro sign
   * @param fractionDigits the number of fraction digits
   * @param portableSymbol the portable symbol
   * @return the {@link CurrencyData} to use
   */
  public static native CurrencyData toCurrencyData(String code, String symbol, int fractionDigits,
      String portableSymbol) /*-{
                             //CHECKSTYLE:OFF
                             return [ code, symbol, fractionDigits, portableSymbol ];
                             //CHECKSTYLE:ON
                             }-*/;

  private boolean currencyFormat = false;

  /**
   *
   * @param pattern the currency pattern
   * @param cdata the {@link CurrencyData}
   * @param userSuppliedPattern <code>true</code> if the pattern is supplied by the user
   */
  protected CustomNumberFormat(final String pattern, final CurrencyData cdata, final boolean userSuppliedPattern,
      final boolean currencyFormat) {
    super(pattern, cdata, userSuppliedPattern);
    this.currencyFormat = currencyFormat;
  }

  /* (non-Javadoc)
   * @see com.google.gwt.i18n.client.NumberFormat#format(boolean, java.lang.StringBuilder, int)
   */
  @Override
  protected void format(final boolean isNegative, final StringBuilder digits, final int scale) {
    super.format(isNegative, digits, scale);
    if (this.currencyFormat) {
      final char decimalSeparator = defaultNumberConstants.monetarySeparator().charAt(0);
      if (digits.toString().endsWith(decimalSeparator + "00")) {
        digits.delete(digits.length() - 3, digits.length());
      }
      if (isNegative) {
        digits.delete(digits.length() - 1, digits.length()); // Delete leading "-"
        digits.insert(0, "- "); // Insert "- " at the front
      }
    }
  }

  /**
   * Parse a String. The String does not start with the expected prefix so we add it first
   * @param text the text to parse
   * @param inOutPos an offset telling us
   * @return the parsed value
   * @throws NumberFormatException if the text cannot be parsed
   */
  @Override
  public double parse(final String text, final int[] inOutPos) throws NumberFormatException {
    //add the positive prefix (euro-sign plus space)
    final String temp = getPositivePrefix() + text;
    //parse the adjusted string
    final double val = super.parse(temp, inOutPos);
    //now here is the tricky bit... during parsing the inOutPos offset was updated based on the modified String
    //but a check is maded to see if the resulting offset is equal to the length of the String we have been passed
    //so we need to update inOutPos by removing the length of the positive prefix
    inOutPos[0] -= getPositivePrefix().length();
    return val;
  }
}

现在我们得到以下结果:

€ 123,45                      when entering 123,45
€ 98.765.432,10               when entering 98765432,1
- € 400,00                    when entering -400