Format credit card in edit text in android

后端 未结 29 1960
耶瑟儿~
耶瑟儿~ 2020-11-30 19:18

How to make EditText accept input in format:

4digit 4digit 4digit 4digit 

I tried Custom format edit text input android to acc

相关标签:
29条回答
  • 2020-11-30 19:49

    I just created a Kotlin class based on Chris Jenkins's answer, and it is usable for this special condition of credit cart input and other related situations. You need to specify the character and digit count for this TextWahcher.

    import android.text.Editable
    import android.text.TextUtils
    import android.text.TextWatcher
    
    /**
     * Formats the watched EditText for digits values
     */
    class DigitFormatWatcher(private val space: Char, private val characterCount: Int) :
        TextWatcher {
        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
        override fun afterTextChanged(s: Editable) {
            // Remove spacing char
            if (s.isNotEmpty() && s.length % (characterCount - 1) == 0) {
                val c = s[s.length - 1]
                if (space == c) {
                    s.delete(s.length - 1, s.length)
                }
            }
            // Insert char where needed.
            if (s.isNotEmpty() && s.length % (characterCount - 1) == 0) {
                val c = s[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(),
                        space.toString()
                    ).size <= (characterCount + 1)
                ) {
                    s.insert(s.length - 1, space.toString())
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-30 19:50

    This solution was implemented for IBAN's but the principle is the same, I tried to correct all the main problems in the answers above, if you find an error feel free to say it, thank you.

    Set the EditText and restrict the characters that can be used:

    private void setEditTextIBAN(View view) {
        editTextIBAN = (EditText) view.findViewById(R.id.client_iban);
        editTextIBAN.setKeyListener(
                DigitsKeyListener.getInstance("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 "));
        editTextIBAN.addTextChangedListener(new IBANTextWatcher());
    }
    

    This is the TextWatcher:

    private class IBANTextWatcher implements TextWatcher {
    
        // means divider position is every 5th symbol
        private static final int DIVIDER_MODULO = 5;
        private static final int GROUP_SIZE = DIVIDER_MODULO - 1;
        private static final char DIVIDER = ' ';
        private static final String STRING_DIVIDER = " ";
        private String previousText = "";
    
        private int deleteLength;
        private int insertLength;
        private int start;
    
        private String regexIBAN = "(\\w{" + GROUP_SIZE + "}" + DIVIDER +
                ")*\\w{1," + GROUP_SIZE + "}";
        private Pattern patternIBAN = Pattern.compile(regexIBAN);
    
        @Override
        public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
            this.previousText = s.toString();
            this.deleteLength = count;
            this.insertLength = after;
            this.start = start;
        }
    
        @Override
        public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
    
        }
    
        @Override
        public void afterTextChanged(final Editable s) {
            String originalString = s.toString();
    
            if (!previousText.equals(originalString) &&
                    !isInputCorrect(originalString)) {
                String newString = previousText.substring(0, start);
                int cursor = start;
    
                if (deleteLength > 0 && s.length() > 0 &&
                        (previousText.charAt(start) == DIVIDER ||
                                start == s.length())) {
                    newString = previousText.substring(0, start - 1);
                    --cursor;
                }
    
                if (insertLength > 0) {
                    newString += originalString.substring(start, start + insertLength);
                    newString = buildCorrectInput(newString);
                    cursor = newString.length();
                }
    
                newString += previousText.substring(start + deleteLength);
                s.replace(0, s.length(), buildCorrectInput(newString));
    
                editTextIBAN.setSelection(cursor);
            }
        }
    
        /**
         * Check if String has the white spaces in the correct positions, meaning
         * if we have the String "123456789" and there should exist a white space
         * every 4 characters then the correct String should be "1234 5678 9".
         *
         * @param s String to be evaluated
         * @return true if string s is written correctly
         */
        private boolean isInputCorrect(String s) {
            Matcher matcherDot = patternIBAN.matcher(s);
            return matcherDot.matches();
        }
    
        /**
         * Puts the white spaces in the correct positions,
         * see the example in {@link IBANTextWatcher#isInputCorrect(String)}
         * to understand the correct positions.
         *
         * @param s String to be corrected.
         * @return String corrected.
         */
        private String buildCorrectInput(String s) {
            StringBuilder sbs = new StringBuilder(
                    s.replaceAll(STRING_DIVIDER, ""));
    
            // Insert the divider in the correct positions
            for (int i = GROUP_SIZE; i < sbs.length(); i += DIVIDER_MODULO) {
                sbs.insert(i, DIVIDER);
            }
    
            return sbs.toString();
        }
    }
    
    0 讨论(0)
  • 2020-11-30 19:51

    You may have figured it out already, but here is what I did. The only method I had to override was AfterTextChanged.

    Check if the form of the credit card is already valid, base case to prevent infinite recursion

    If the form is not valid, remove all whitespace, and copy over into another string, inserting white space where appropriate.

    Then simply replace the editable with your new string.

    If you need code for a particular step, feel free to ask.

    And Preethi, the reason you can't delete spaces is because you can't change text in the onTextChanged callback. From the developer site:

    public abstract void onTextChanged (CharSequence s, int start, int before, int count) Added in API level 1

    This method is called to notify you that, within s, the count characters beginning at start have just replaced old text that had length before. It is an error to attempt to make changes to s from this callback.

    0 讨论(0)
  • 2020-11-30 19:52
    import android.text.Editable;
    import android.text.TextWatcher;
    import android.widget.EditText;`
    
    public class CreditCard implements TextWatcher
    {
        EditText editText;
    
        public CreditCard(EditText editText)
        {
            this.editText = editText;
        }
    
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
        }
    
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
    
        }
    
        @Override
        public void afterTextChanged(Editable s) {
            try
            {
                editText.removeTextChangedListener(this);
    
                String str = editText.getText().toString().replaceAll("-", "");
    
                editText.setText(setDash(str));
    
                editText.setSelection(editText.getText().toString().length());
    
                editText.addTextChangedListener(this);
                return;
            }
    
            catch (Exception ex)
            {
                ex.printStackTrace();
                editText.addTextChangedListener(this);
            }
    
        }
    
        public static String setDash(String value)
        {
            String str = "";
            int j = 0;
    
            for (int i = 0;i<value.length(); i++)
            {
                j++;
    
                if (j == 5)
                {
                    str = str+"-";
                    j = 1;
                }
    
                str = str + value.charAt(i);
            }
    
            return str;
    
        }
    
        public static String trimDashOfString(String string)
        {
            if (string.contains("-")) {
                return string.replace("-", "");
                } else {
                return string;
            }
    
        }
    }
    
    0 讨论(0)
  • 2020-11-30 19:54

    Not sure the TextWatcher is the right thing to use - we should use InputFilter

    According to Android documentation, TextWatcher should be used for an external usage example : one [EditView] for password input + one [TextView] view which displays "weak", "strong", etc...

    For Credit Card Format I am using InputFilter:

    public class CreditCardInputFilter implements InputFilter {
        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            if (dest != null & dest.toString().trim().length() > 24) return null;
            if (source.length() == 1 && (dstart == 4 || dstart == 9 || dstart == 14))
                return " " + new String(source.toString());
            return null; // keep original
        }
    }
    

    And combine with a length filter (Android SDK) :

    mEditCardNumber.setFilters(new InputFilter[]{
         new InputFilter.LengthFilter(24),
         new CreditCardInputFilter(),
    });
    

    This handle the case when typing and removing a digit.

    (!) But this does not handle the case for a copy/paste of an entire string, this one should be done in a different InputFilter class

    Hope it helps !

    0 讨论(0)
  • 2020-11-30 19:54
    class XYZ : TextWatcher {
    
    private val formatSymbols = DecimalFormatSymbols(Locale.getDefault())
    
    private lateinit var formatter: DecimalFormat
    
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        .
        .
        formatSymbols.groupingSeparator = ' '
        formatter = DecimalFormat("####,####", formatSymbols)
        .
        .
    }
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        editText.addTextChangedListener(this)
    }
    
    override fun afterTextChanged(s: Editable?) {
        if (editText.error != null) {
            editText.error = null
        }
        editText.removeTextChangedListener(this)
        try {
            var originalString = s.toString()
            if (originalString.contains(" ")) {
                originalString = originalString.replace(" ", "", true)
            }
            val longVal: Long? = originalString.toLong()
            val formattedString = formatter.format(longVal)
            editText.setText(formattedString)
            editText.setSelection(editText.text.length)
        } catch (error: NumberFormatException) {
            // Print Error Or Do Whatever you want.
        }
        editText.addTextChangedListener(this)
    }
    
    }
    
    0 讨论(0)
提交回复
热议问题