Ok, here is a better way to deal with Currency formats, delete-backward keystroke.
The code is based on @androidcurious' code above...
But, deals with some problems related to backwards-deletion and some parse exceptions:
http://miguelt.blogspot.ca/2013/01/textwatcher-for-currency-masksformatting.html
[UPDATE] The previous solution had some problems...
This is a better solutoin: http://miguelt.blogspot.ca/2013/02/update-textwatcher-for-currency.html
And... here are the details:
This approach is better since it uses the conventional Android mechanisms.
The idea is to format values after the user exists the View.
Define an InputFilter to restrict the numeric values – this is required in most cases because the screen is not large enough to accommodate long EditText views.
This can be a static inner class or just another plain class:
/** Numeric range Filter. */
class NumericRangeFilter implements InputFilter {
/** Maximum value. */
private final double maximum;
/** Minimum value. */
private final double minimum;
/** Creates a new filter between 0.00 and 999,999.99. */
NumericRangeFilter() {
this(0.00, 999999.99);
}
/** Creates a new filter.
* @param p_min Minimum value.
* @param p_max Maximum value.
*/
NumericRangeFilter(double p_min, double p_max) {
maximum = p_max;
minimum = p_min;
}
@Override
public CharSequence filter(
CharSequence p_source, int p_start,
int p_end, Spanned p_dest, int p_dstart, int p_dend
) {
try {
String v_valueStr = p_dest.toString().concat(p_source.toString());
double v_value = Double.parseDouble(v_valueStr);
if (v_value<=maximum && v_value>=minimum) {
// Returning null will make the EditText to accept more values.
return null;
}
} catch (NumberFormatException p_ex) {
// do nothing
}
// Value is out of range - return empty string.
return "";
}
}
Define a class (inner static or just a class) that will implement View.OnFocusChangeListener.
Note that I'm using an Utils class - the implementation can be found at "Amounts, Taxes".
/** Used to format the amount views. */
class AmountOnFocusChangeListener implements View.OnFocusChangeListener {
@Override
public void onFocusChange(View p_view, boolean p_hasFocus) {
// This listener will be attached to any view containing amounts.
EditText v_amountView = (EditText)p_view;
if (p_hasFocus) {
// v_value is using a currency mask - transfor over to cents.
String v_value = v_amountView.getText().toString();
int v_cents = Utils.parseAmountToCents(v_value);
// Now, format cents to an amount (without currency mask)
v_value = Utils.formatCentsToAmount(v_cents);
v_amountView.setText(v_value);
// Select all so the user can overwrite the entire amount in one shot.
v_amountView.selectAll();
} else {
// v_value is not using a currency mask - transfor over to cents.
String v_value = v_amountView.getText().toString();
int v_cents = Utils.parseAmountToCents(v_value);
// Now, format cents to an amount (with currency mask)
v_value = Utils.formatCentsToCurrency(v_cents);
v_amountView.setText(v_value);
}
}
}
This class will remove the currency format when editing - relying on standard mechanisms.
When the user exits, the currency format is re-applied.
It's better to define some static variables to minimize the number of instances:
static final InputFilter[] FILTERS = new InputFilter[] {new NumericRangeFilter()};
static final View.OnFocusChangeListener ON_FOCUS = new AmountOnFocusChangeListener();
Finally, within the onCreateView(...):
EditText mAmountView = ....
mAmountView.setFilters(FILTERS);
mAmountView.setOnFocusChangeListener(ON_FOCUS);
You can reuse FILTERS and ON_FOCUS on any number of EditText views.
Here is the Utils class:
public class Utils {
private static final NumberFormat FORMAT_CURRENCY = NumberFormat.getCurrencyInstance();
/** Parses an amount into cents.
* @param p_value Amount formatted using the default currency.
* @return Value as cents.
*/
public static int parseAmountToCents(String p_value) {
try {
Number v_value = FORMAT_CURRENCY.parse(p_value);
BigDecimal v_bigDec = new BigDecimal(v_value.doubleValue());
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
return v_bigDec.movePointRight(2).intValue();
} catch (ParseException p_ex) {
try {
// p_value doesn't have a currency format.
BigDecimal v_bigDec = new BigDecimal(p_value);
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
return v_bigDec.movePointRight(2).intValue();
} catch (NumberFormatException p_ex1) {
return -1;
}
}
}
/** Formats cents into a valid amount using the default currency.
* @param p_value Value as cents
* @return Amount formatted using a currency.
*/
public static String formatCentsToAmount(int p_value) {
BigDecimal v_bigDec = new BigDecimal(p_value);
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
v_bigDec = v_bigDec.movePointLeft(2);
String v_currency = FORMAT_CURRENCY.format(v_bigDec.doubleValue());
return v_currency.replace(FORMAT_CURRENCY.getCurrency().getSymbol(), "").replace(",", "");
}
/** Formats cents into a valid amount using the default currency.
* @param p_value Value as cents
* @return Amount formatted using a currency.
*/
public static String formatCentsToCurrency(int p_value) {
BigDecimal v_bigDec = new BigDecimal(p_value);
v_bigDec = v_bigDec.setScale(2, BigDecimal.ROUND_HALF_UP);
v_bigDec = v_bigDec.movePointLeft(2);
return FORMAT_CURRENCY.format(v_bigDec.doubleValue());
}
}