How to make internationalized EditText for numbers input in Android?

Android has evolved a lot till date but still few things are missing for developers and that we have to customize by ourself.

Out of those few things one is Internationalized EditText for numbers. Now few of you might have a question, what is internationalization of numbers ? So let me give an example of representation of internationalization of numbers – i.e.,

for India  Р123.4

for Germany – 123,4

for switzerland – 123.4

Now I guess you have an understanding about it. In context with Android, android still not provides any direct option to allow softkeyboard to enter “,” instead of “.” in the focused EditText as per different locale. So I just made a custom EditText with InputFilter to solve this issue as I needed that. Lets go through it . So the My first class is NumericEditText .

public class NumericEditText extends AppCompatEditText {
    Context context;
    private Locale PREFFERD_LOCALE = Locale.GERMANY;
    private boolean is2DigitFractionRequired = false;

    public void setAllow2DigitFraction(boolean allow2DigitFraction) {
        is2DigitFractionRequired = allow2DigitFraction;
        setInputFilter();
    }

    public NumericEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        setInputFilter();
    }

    public NumericEditText(Context context) {
        super(context);
        this.context = context;
        setInputFilter();
    }

    public NumericEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        setInputFilter();
    }

    private void setInputFilter() {
        setFilters(new InputFilter[]{new LocalDecimalInputFilter(PREFFERD_LOCALE, is2DigitFractionRequired)});
    }

    public Number getNumber() {
        String strText = getText().toString();
        return NumberFormatUtil.getNumber(strText, PREFFERD_LOCALE);
    }

    public float getFloat() {
        return getNumber().floatValue();
    }

    public double getDouble() {
        return getNumber().doubleValue();
    }

    public long getLong() {
        return getNumber().longValue();
    }

    public int getInt() {
        return getNumber().intValue();
    }
}

class LocalDecimalInputFilter implements InputFilter {

    private final Locale PREFFERD_LOCALE;
    private final char DECIMAL_SEPARATOR;
    private final char GROUPING_SEPARATOR;
    private boolean is2DigitFractionRequired = false;
    private final int ALLOWED_DECIMAL_PLACES = 2;

    public LocalDecimalInputFilter(Locale locale, boolean is2DigitFractionRequired) {
        PREFFERD_LOCALE = locale;
        DECIMAL_SEPARATOR = NumberFormatUtil.getDecimalSeparator(PREFFERD_LOCALE);
        GROUPING_SEPARATOR = NumberFormatUtil.getGroupingSeparator(PREFFERD_LOCALE);

        this.is2DigitFractionRequired = is2DigitFractionRequired;
    }

    private boolean isCharAllowed(char c, String text) {
        return (DECIMAL_SEPARATOR == c && !text.contains(String.valueOf(c))) || Character.isDigit(c);
    }

    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        final StringBuilder filteredStringBuilder = new StringBuilder(end - start);

        String text = dest.toString();
        for (int i = start; i < end; i++) {
            char c = source.charAt(i);
            if (isCharAllowed(c, text))
                filteredStringBuilder.append(c);
        }

        if (is2DigitFractionRequired) {
            int index = text.indexOf(String.valueOf(DECIMAL_SEPARATOR));
            if (index != -1) {
                if (text.substring(index + 1).length() == ALLOWED_DECIMAL_PLACES) {
                    return "";
                }
            }
        }
        return filteredStringBuilder.toString();
    }
}

This is pretty descriptive so I am not describing it any further but there is another class used called NumberFormatUtil .

public class NumberFormatUtil {

    private static final int MINIMUM_FRACTION_DIGITS = 0;
    private static final int MAXIMUM_FRACTION_DIGITS = 2;
    private static final int ARABIC_DECIMAL_SEPARATOR_CODE = 1643;

    private static NumberFormat getNumberFormattor(Locale locale) {
        NumberFormat formatter = DecimalFormat.getInstance(locale);
        formatter.setMaximumFractionDigits(MAXIMUM_FRACTION_DIGITS);
        formatter.setMinimumFractionDigits(MINIMUM_FRACTION_DIGITS);
        return formatter;
    }

    public static Number getNumber(String number, Locale locale) {
        NumberFormat formatter = getNumberFormattor(locale);
        Number parsedNumber = 0;
        if (number != null && !number.isEmpty()) {
            try {
                parsedNumber = formatter.parse(number);
            } catch (ParseException pe) {
                pe.printStackTrace();
            }
        }
        return parsedNumber;
    }

    public static String formatNumber(double number, Locale locale) {
        return getNumberFormattor(locale).format(number);
    }

    public static String formatNumber(String number, Locale locale) {
        return getNumberFormattor(locale).format(getNumber(number, locale));
    }

    public static char getGroupingSeparator(Locale locale) {
        return DecimalFormatSymbols.getInstance(locale).getGroupingSeparator();
    }

    public static char getDecimalSeparator(Locale locale) {
        //This fix is for Arabic locale
        if ((int) DecimalFormatSymbols.getInstance(locale).getDecimalSeparator() == ARABIC_DECIMAL_SEPARATOR_CODE) {
            return ',';
        } else {
            return DecimalFormatSymbols.getInstance(locale).getDecimalSeparator();
        }
    }
}

NumberFormatUtil is basically for formatting of any number based on the passed locale, so you can use this class’s factory methods to display numbers in internationalized format.

Now you are all set to use it,¬† this is how you can use this in our layouts –

<com.views.NumericEditText
                        android:id="@+id/netSample"
                        android:layout_width="0dp"
                        android:layout_gravity="center_vertical"
                        android:layout_height="wrap_content"
                        android:layout_weight="4"
                        style="@style/edit_text_decimal_number_style"
                        android:gravity="center_vertical" />

Oh one more thing, I have used a separate style also for this view.

<style name="edit_text_decimal_number_style" parent="edit_text_style">
    <item name="android:digits">0123456789.,</item>
    <item name="android:inputType">phone</item>
</style>

Please make sure you will use this style with these minimal items as it is otherwise this view will misbehave.

I have tested it in almost all categories of android phones and tablets and found it working .

If you have any further query regarding this, feel free to buzz me.

Happy coding!!!

 

Leave a Reply

Your email address will not be published. Required fields are marked *