Format credit card in edit text in android Format credit card in edit text in android android android

Format credit card in edit text in android


After finding multiple answers that are 'OK'. I moved towards a better TextWatcher which is designed to work correctly and independently from the TextView.

TextWatcher class is as follows:

/** * Formats the watched EditText to a credit card number */public static class FourDigitCardFormatWatcher implements TextWatcher {    // Change this to what you want... ' ', '-' etc..    private static final char space = ' ';    @Override    public void onTextChanged(CharSequence s, int start, int before, int count) {    }    @Override    public void beforeTextChanged(CharSequence s, int start, int count, int after) {    }    @Override    public void afterTextChanged(Editable s) {        // Remove spacing char        if (s.length() > 0 && (s.length() % 5) == 0) {            final char c = s.charAt(s.length() - 1);            if (space == c) {                s.delete(s.length() - 1, s.length());            }        }        // Insert char where needed.        if (s.length() > 0 && (s.length() % 5) == 0) {            char c = s.charAt(s.length() - 1);            // Only if its a digit where there should be a space we insert a space            if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 3) {                s.insert(s.length() - 1, String.valueOf(space));            }        }    }}

Then add it to your TextView as you would any other TextWatcher.

{  //...  mEditTextCreditCard.addTextChangedListener(new FourDigitCardFormatWatcher()); }

This will auto delete the space sensibly going back so the user can actually do less keystrokes when editing.

Caveat

If you are using inputType="numberDigit" this will disable the '-' and ' ' chars, so I recommend using, inputType="phone". This enables other chars, but just use a custom inputfilter and problem solved.


Demo - How this works

Example on github.com

Late answer, but I guess it may helpful for somebody:

    cardNumberEditText.addTextChangedListener(new TextWatcher() {        private static final int TOTAL_SYMBOLS = 19; // size of pattern 0000-0000-0000-0000        private static final int TOTAL_DIGITS = 16; // max numbers of digits in pattern: 0000 x 4        private static final int DIVIDER_MODULO = 5; // means divider position is every 5th symbol beginning with 1        private static final int DIVIDER_POSITION = DIVIDER_MODULO - 1; // means divider position is every 4th symbol beginning with 0        private static final char DIVIDER = '-';        @Override        public void beforeTextChanged(CharSequence s, int start, int count, int after) {            // noop        }        @Override        public void onTextChanged(CharSequence s, int start, int before, int count) {            // noop        }        @Override        public void afterTextChanged(Editable s) {            if (!isInputCorrect(s, TOTAL_SYMBOLS, DIVIDER_MODULO, DIVIDER)) {                s.replace(0, s.length(), buildCorrectString(getDigitArray(s, TOTAL_DIGITS), DIVIDER_POSITION, DIVIDER));            }        }        private boolean isInputCorrect(Editable s, int totalSymbols, int dividerModulo, char divider) {            boolean isCorrect = s.length() <= totalSymbols; // check size of entered string            for (int i = 0; i < s.length(); i++) { // check that every element is right                if (i > 0 && (i + 1) % dividerModulo == 0) {                    isCorrect &= divider == s.charAt(i);                } else {                    isCorrect &= Character.isDigit(s.charAt(i));                }            }            return isCorrect;        }        private String buildCorrectString(char[] digits, int dividerPosition, char divider) {            final StringBuilder formatted = new StringBuilder();            for (int i = 0; i < digits.length; i++) {                if (digits[i] != 0) {                    formatted.append(digits[i]);                    if ((i > 0) && (i < (digits.length - 1)) && (((i + 1) % dividerPosition) == 0)) {                        formatted.append(divider);                    }                }            }            return formatted.toString();        }        private char[] getDigitArray(final Editable s, final int size) {            char[] digits = new char[size];            int index = 0;            for (int i = 0; i < s.length() && index < size; i++) {                char current = s.charAt(i);                if (Character.isDigit(current)) {                    digits[index] = current;                    index++;                }            }            return digits;        }    });

this works perfectly with start-string/end-string/mid-string editing, also paste works perfectly.


I modified Chris Jenkins answer to make it more robust. With this, even if the user edits the middle of the text, the spacing characters are still inserted (and automatically removed on wrong places) correctly.

To make this work correctly, make sure the EditText attributes are set as follows (note the space on digits):

android:digits="01234 56789"android:inputType="number"android:maxLength="19"

Then here is the TextWatcher you need. The anonymous class can also be made static since this is independent of the EditText.

    yourTextView.addTextChangedListener(new TextWatcher() {        private static final char space = ' ';        @Override        public void onTextChanged(CharSequence s, int start, int before, int count) {        }        @Override        public void beforeTextChanged(CharSequence s, int start, int count, int after) {        }        @Override        public void afterTextChanged(Editable s) {            // Remove all spacing char            int pos = 0;            while (true) {                if (pos >= s.length()) break;                if (space == s.charAt(pos) && (((pos + 1) % 5) != 0 || pos + 1 == s.length())) {                    s.delete(pos, pos + 1);                } else {                    pos++;                }            }            // Insert char where needed.            pos = 4;            while (true) {                if (pos >= s.length()) break;                final char c = s.charAt(pos);                // Only if its a digit where there should be a space we insert a space                if ("0123456789".indexOf(c) >= 0) {                    s.insert(pos, "" + space);                }                pos += 5;            }        }    });