package faranjit.currency.edittext; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; import android.text.Editable; import android.text.InputType; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.widget.EditText; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.Currency; import java.util.Locale; /** * Created by bulent.turkmen on 8/9/2016. */ public class CurrencyEditText extends EditText { private char mGroupDivider; private char mMonetaryDivider; private String mLocale = ""; private boolean mShowSymbol; private boolean mEraseWhenZero; private int mDecimalPoints; private char groupDivider; private char monetaryDivider; private Locale locale; private DecimalFormat numberFormat; private String currencySymbol; private int fractionDigit; public CurrencyEditText(Context context, AttributeSet attrs) { super(context, attrs); this.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.currencyEditText, 0, 0); try { if (a.getString(R.styleable.currencyEditText_groupDivider) != null) { this.mGroupDivider = a.getString(R.styleable.currencyEditText_groupDivider).charAt(0); this.groupDivider = mGroupDivider; } if (a.getString(R.styleable.currencyEditText_monetaryDivider) != null) { this.mMonetaryDivider = a.getString(R.styleable.currencyEditText_monetaryDivider).charAt(0); this.monetaryDivider = mMonetaryDivider; } if (a.getString(R.styleable.currencyEditText_locale) == null) this.locale = getDefaultLocale(); else this.mLocale = a.getString(R.styleable.currencyEditText_locale); if (a.getString(R.styleable.currencyEditText_showSymbol) != null) this.mShowSymbol = a.getBoolean(R.styleable.currencyEditText_showSymbol, false); if (a.getString(R.styleable.currencyEditText_eraseWhenZero) != null) this.mEraseWhenZero = a.getBoolean(R.styleable.currencyEditText_eraseWhenZero, false); if (a.getString(R.styleable.currencyEditText_decimalPoints) != null) { this.mDecimalPoints = a.getInt(R.styleable.currencyEditText_decimalPoints, 0); this.fractionDigit = mDecimalPoints; } if (mLocale.equals("")) { locale = getDefaultLocale(); } else { if (mLocale.contains("-")) mLocale = mLocale.replace("-", "_"); String[] l = mLocale.split("_"); if (l.length > 1) { locale = new Locale(l[0], l[1]); } else { locale = new Locale("", mLocale); } } initSettings(); } finally { a.recycle(); } this.addTextChangedListener(onTextChangeListener); } /*** * If user does not provide a valid locale it throws IllegalArgumentException. * * If throws an IllegalArgumentException the locale sets to default locale */ private void initSettings() { boolean success = false; while (!success) { try { if (fractionDigit == 0) { fractionDigit = Currency.getInstance(locale).getDefaultFractionDigits(); } DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale); if (mGroupDivider > 0) symbols.setGroupingSeparator(mGroupDivider); groupDivider = symbols.getGroupingSeparator(); if (mMonetaryDivider > 0) symbols.setMonetaryDecimalSeparator(mMonetaryDivider); monetaryDivider = symbols.getMonetaryDecimalSeparator(); currencySymbol = symbols.getCurrencySymbol(); DecimalFormat df = (DecimalFormat) DecimalFormat.getCurrencyInstance(locale); numberFormat = new DecimalFormat(df.toPattern(), symbols); if (mDecimalPoints > 0) { numberFormat.setMinimumFractionDigits(mDecimalPoints); } success = true; } catch (IllegalArgumentException e) { Log.e(getClass().getCanonicalName(), e.getMessage()); locale = getDefaultLocale(); } } } @SuppressWarnings("deprecation") private Locale getDefaultLocale() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) return getContext().getResources().getConfiguration().getLocales().get(0); else return getContext().getResources().getConfiguration().locale; } /*** *It resets text currently displayed If user changes separators or locale etc. */ private void resetText() { String s = getText().toString(); if (s.isEmpty()) { initSettings(); return; } s = s.replace(groupDivider, '\u0020').replace(monetaryDivider, '\u0020') .replace(".", "").replace(" ", "") .replace(currencySymbol, "").trim(); try { initSettings(); s = format(s); removeTextChangedListener(onTextChangeListener); setText(s); setSelection(s.length()); addTextChangedListener(onTextChangeListener); } catch (NumberFormatException e) { Log.e(getClass().getCanonicalName(), e.getMessage()); } catch (NullPointerException e) { Log.e(getClass().getCanonicalName(), e.getMessage()); } } private TextWatcher onTextChangeListener = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.length() == 0) return; removeTextChangedListener(this); /* * Clear input to get clean text before format * '\u0020' is empty character */ String text = s.toString(); // thanks for: https://stackoverflow.com/a/10372905/1595442 text = text.replaceAll("[^\\d]", ""); if (checkErasable(text)) { getText().clear(); } else { try { text = format(text); } catch (NumberFormatException e) { Log.e(getClass().getCanonicalName(), e.getMessage()); } catch (NullPointerException e) { Log.e(getClass().getCanonicalName(), e.getMessage()); } setText(text); setSelection(text.length()); } addTextChangedListener(this); } @Override public void afterTextChanged(Editable s) { } }; private String format(String text) throws NumberFormatException, NullPointerException { if (mShowSymbol) return numberFormat.format(Double.parseDouble(text) / Math.pow(10, fractionDigit)); else return numberFormat.format(Double.parseDouble(text) / Math.pow(10, fractionDigit)).replace(currencySymbol, ""); } private boolean checkErasable(String text) { if (mEraseWhenZero) { try { return Integer.valueOf(text) == 0; } catch (NumberFormatException e) { Log.e(getClass().getCanonicalName(), e.getMessage()); } } return false; } /*** * returns the decimal separator for current locale * for example; input value 1,234.56 * returns ',' * * @return decimal separator char */ public char getGroupDivider() { return groupDivider; } /*** * sets how to divide decimal value and fractions * for example; If you want formatting like this * for input value 1,234.56 * set ',' * @param groupDivider char */ public void setGroupDivider(char groupDivider) { this.mGroupDivider = groupDivider; resetText(); } /*** * returns the monetary separator for current locale * for example; input value 1,234.56 * returns '.' * * @return monetary separator char */ public char getMonetaryDivider() { return monetaryDivider; } /*** * sets how to divide decimal value and fractions * for example; If you want formatting like this * for input value 1,234.56 * set '.' * @param monetaryDivider char */ public void setMonetaryDivider(char monetaryDivider) { this.mMonetaryDivider = monetaryDivider; resetText(); } /*** * * @return current locale */ public Locale getLocale() { return locale; } /*** * Sets locale which desired currency format * * @param locale Desired locale */ public void setLocale(Locale locale) { this.locale = locale; resetText(); } /** * @return true if currency symbol of current locale is showing */ public boolean showSymbol() { return this.mShowSymbol; } /*** * Sets if currency symbol of current locale shows * * @param showSymbol Set true if you want to see currency symbol */ public void showSymbol(boolean showSymbol) { this.mShowSymbol = showSymbol; resetText(); } /** * @return double value for current text */ public double getCurrencyDouble() throws NumberFormatException, NullPointerException { String text = getText().toString(); text = text.replace(groupDivider, '\u0020').replace(monetaryDivider, '\u0020') .replace(".", "").replace(" ", "") .replace(currencySymbol, "").trim(); if (showSymbol()) return Double.parseDouble(text.replace(currencySymbol, "")) / Math.pow(10, fractionDigit); else return Double.parseDouble(text) / Math.pow(10, fractionDigit); } /** * @return String value for current text */ public String getCurrencyText() { if (showSymbol()) return getText().toString().replace(currencySymbol, ""); else return getText().toString(); } /** * @return Int value of count of monetary decimals. */ public int getFractionDigit() { return fractionDigit; } /** * Changes count of monetary decimal. * For instance; * if you set 4, it formats like 12,3456 for 123456 * * @param decimalPoints The count of monetary decimals */ public void setDecimals(int decimalPoints) { this.mDecimalPoints = decimalPoints; this.fractionDigit = decimalPoints; resetText(); } }