在android中的编辑文本中格式化信用卡

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11790102/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 08:53:22  来源:igfitidea点击:

Format credit card in edit text in android

androidformattingandroid-edittext

提问by Preethi

How to make EditTextaccept input in format:

如何以EditText格式接受输入:

4digit 4digit 4digit 4digit 

I tried Custom format edit text input android to accept credit card number, but unfortunately I was unable to delete the spaces. Whenever there is a space, I could not to delete it. Please help me in finding out the issue.

我尝试了自定义格式编辑文本输入 android 来接受信用卡号,但不幸的是我无法删除空格。每当有空格时,我都无法删除它。请帮我找出问题所在。

回答by Chris.Jenkins

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

在找到“OK”的多个答案之后。我转向了一个更好的 TextWatcher,它被设计为可以正常工作并且独立于TextView.

TextWatcher class is as follows:

TextWatcher 类如下:

/**
 * 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.

然后将其添加到您的 TextView 中,就像您添加任何其他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.

如果您使用inputType="numberDigit"它会禁用 '-' 和 ' ' 字符,所以我建议使用, inputType="phone". 这可以启用其他字符,但只需使用自定义输入过滤器即可解决问题。

回答by Igor Tyulkanov

Demo - How this works

Demo - How this works

Example on github.com

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 pasteworks perfectly.

这与开始字符串/结束字符串/中间字符串编辑完美配合,也可以完美粘贴

回答by Randy Sugianto 'Yuku'

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.

我修改了 Chris Jenkins 的答案,使其更加健壮。这样,即使用户编辑文本的中间,空格字符仍会正确插入(并在错误的位置自动删除)。

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

要使其正常工作,请确保EditText属性设置如下(注意 上的空格digits):

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

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

那么这里就是TextWatcher你需要的。匿名类也可以是静态的,因为它独立于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;
            }
        }
    });

回答by Umran

Here is a cleaner solution using regular expressions. Although regular expressions can be inefficient, they would be sufficient in this case since it's processing a string of at most 19 characters, even if the processing occurs after each key press.

这是使用正则表达式的更清洁的解决方案。尽管正则表达式可能效率低下,但在这种情况下它们就足够了,因为它处理最多 19 个字符的字符串,即使处理发生在每次按键之后。

editTxtCardNumber.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence s, int arg1, int arg2,
            int arg3) { }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

    @Override
    public void afterTextChanged(Editable s) {
        String initial = s.toString();
        // remove all non-digits characters
        String processed = initial.replaceAll("\D", "");
        // insert a space after all groups of 4 digits that are followed by another digit
        processed = processed.replaceAll("(\d{4})(?=\d)", " ");
        // to avoid stackoverflow errors, check that the processed is different from what's already
        //  there before setting
        if (!initial.equals(processed)) {
            // set the value
            s.replace(0, initial.length(), processed);
        }

    }

});

回答by MW.

I'm adding my solution to the list. As far as I am aware, it has no drawback; you can edit in the middle, delete spacing characters, copy and paste into it etc.

我正在将我的解决方案添加到列表中。据我所知,它没有缺点;您可以在中间进行编辑,删除间距字符,复制并粘贴到其中等。

To allow editing to take place anywhere in the string, and to maintain cursor position, the Editable is traversed and all whitespace (if any) are taken out one by one. New whitespace is then added at appropriate positions. This will ensure that the cursor moves along with the changes made to the contents.

为了允许在字符串中的任何位置进行编辑,并保持光标位置,可编辑对象被遍历,所有空格(如果有)都被一一删除。然后在适当的位置添加新的空白。这将确保光标随着对内容所做的更改而移动。

import java.util.LinkedList;


import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;


/**
 * Formats the watched EditText to groups of characters, with spaces between them.
 */
public class GroupedInputFormatWatcher implements TextWatcher {

    private static final char SPACE_CHAR = ' ';
    private static final String SPACE_STRING = String.valueOf(SPACE_CHAR);
    private static final int GROUPSIZE = 4;

    /**
     * Breakdown of this regexp:
     * ^             - Start of the string
     * (\d{4}\s)*  - A group of four digits, followed by a whitespace, e.g. "1234 ". Zero or more times.
     * \d{0,4}      - Up to four (optional) digits.
     * (?<!\s)$     - End of the string, but NOT with a whitespace just before it.
     * 
     * Example of matching strings:
     *  - "2304 52"
     *  - "2304"
     *  - ""
     */
    private final String regexp = "^(\d{4}\s)*\d{0,4}(?<!\s)$";
    private boolean isUpdating = false;

    private final EditText editText;

    public GroupedInputFormatWatcher(EditText editText) {
        this.editText = editText;
    }

    @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) {
        String originalString = s.toString();

        // Check if we are already updating, to avoid infinite loop.
        // Also check if the string is already in a valid format.
        if (isUpdating || originalString.matches(regexp)) {
            return;
        }

        // Set flag to indicate that we are updating the Editable.
        isUpdating = true;

        // First all whitespaces must be removed. Find the index of all whitespace.
        LinkedList<Integer> spaceIndices = new LinkedList <Integer>();
        for (int index = originalString.indexOf(SPACE_CHAR); index >= 0; index = originalString.indexOf(SPACE_CHAR, index + 1)) {
            spaceIndices.offerLast(index);
        }

        // Delete the whitespace, starting from the end of the string and working towards the beginning.
        Integer spaceIndex = null;
        while (!spaceIndices.isEmpty()) {
            spaceIndex = spaceIndices.removeLast();
            s.delete(spaceIndex, spaceIndex + 1);
        }

        // Loop through the string again and add whitespaces in the correct positions
        for(int i = 0; ((i + 1) * GROUPSIZE + i) < s.length(); i++) {
            s.insert((i + 1) * GROUPSIZE + i, SPACE_STRING);
        }

        // Finally check that the cursor is not placed before a whitespace.
        // This will happen if, for example, the user deleted the digit '5' in
        // the string: "1234 567".
        // If it is, move it back one step; otherwise it will be impossible to delete
        // further numbers.
        int cursorPos = editText.getSelectionStart();
        if (cursorPos > 0 && s.charAt(cursorPos - 1) == SPACE_CHAR) {
            editText.setSelection(cursorPos - 1);
        }

        isUpdating = false;
    }
}

回答by Dounaka DK

Not sure the TextWatcheris the right thing to use - we should use InputFilter

不确定TextWatcher是否适合使用 - 我们应该使用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...

根据 Android 文档,TextWatcher 应该用于外部使用示例:一个用于密码输入的 [EditView] +一个显示“弱”、“强”等的 [TextView] 视图...

For Credit Card FormatI am using InputFilter:

对于信用卡格式,我使用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) :

并结合长度过滤器(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

(!) 但是这不能处理复制/粘贴整个字符串的情况,这应该在不同的 InputFilter 类中完成

Hope it helps !

希望能帮助到你 !

回答by Tom

This implementation ensures correct placement of spacing chars, even if the user edits mid-string. Other characters that show up on the soft keyboard (such as dash) are also supported; that is, the user can't enter them. One improvement that could be made: this implementation doesn't allow for the deletion of spacing characters mid-string.

即使用户编辑中间字符串,此实现也可确保正确放置间距字符。还支持软键盘上显示的其他字符(如破折号);也就是说,用户不能输入它们。可以进行的一项改进:此实现不允许删除字符串中间的空格字符。

public class CreditCardTextWatcher implements TextWatcher {

    public static final char SPACING_CHAR = '-'; // Using a Unicode character seems to stuff the logic up.

    @Override
    public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { }

    @Override
    public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { }

    @Override
    public void afterTextChanged(final Editable s) {
        if (s.length() > 0) {

            // Any changes we make to s in here will cause this method to be run again.  Thus we only make changes where they need to be made,
            // otherwise we'll be in an infinite loop.

            // Delete any spacing characters that are out of place.
            for (int i=s.length()-1; i>=0; --i) {
                if (s.charAt(i) == SPACING_CHAR  // There is a spacing char at this position ,
                        && (i+1 == s.length()    // And it's either the last digit in the string (bad),
                        || (i+1) % 5 != 0)) {    // Or the position is not meant to contain a spacing char?

                    s.delete(i,i+1);
                }
            }

            // Insert any spacing characters that are missing.
            for (int i=14; i>=4; i-=5) {
                if (i < s.length() && s.charAt(i) != SPACING_CHAR) {
                    s.insert(i, String.valueOf(SPACING_CHAR));
                }
            }
        }
    }
}

Works well with an appropriate PasswordTransformationMethodimplementation to mask CC digits.

与适当的PasswordTransformationMethod实现一起很好地掩盖 CC 数字。

回答by epool

I just did the next implementation and works well for me, even with pasting and typing new text in any position of the EditText.

我刚刚做了下一个实现并且对我来说效果很好,即使在EditText.

Gist file

要点文件

/**
 * Text watcher for giving "#### #### #### ####" format to edit text.
 * Created by epool on 3/14/16.
 */
public class CreditCardFormattingTextWatcher implements TextWatcher {

    private static final String EMPTY_STRING = "";
    private static final String WHITE_SPACE = " ";
    private String lastSource = EMPTY_STRING;

    @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) {
        String source = s.toString();
        if (!lastSource.equals(source)) {
            source = source.replace(WHITE_SPACE, EMPTY_STRING);
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < source.length(); i++) {
                if (i > 0 && i % 4 == 0) {
                    stringBuilder.append(WHITE_SPACE);
                }
                stringBuilder.append(source.charAt(i));
            }
            lastSource = stringBuilder.toString();
            s.replace(0, s.length(), lastSource);
        }
    }

}

Usage:editText.addTextChangedListener(new CreditCardFormattingTextWatcher());

用法:editText.addTextChangedListener(new CreditCardFormattingTextWatcher());

回答by Gaurav Saluja

After searching a lot and not getting any satisfactory answer to meet my needs, I ended up writing my own function.

在搜索了很多并且没有得到任何满意的答案来满足我的需求之后,我最终编写了自己的函数。

Here is an example to format entered credit card details based on the type of card being entered. Currently it takes care of Visa, MasterCard and American Express for the purpose of formatting.

以下是根据输入的卡类型格式化输入的信用卡详细信息的示例。目前它负责处理 Visa、MasterCard 和 American Express 以进行格式化。

    editTxtCardNumber.addTextChangedListener(new TextWatcher() {

        private boolean spaceDeleted;

        @Override
        public void onTextChanged(CharSequence s, int arg1, int arg2,
                int arg3) {

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            CharSequence charDeleted = s.subSequence(start, start + count);
            spaceDeleted = " ".equals(charDeleted.toString());
        }

        @Override
        public void afterTextChanged(Editable editable) {

            if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '3') {
                editTxtCardNumber.setFilters(new InputFilter[] { new InputFilter.LengthFilter(Constants.MAX_LENGTH_CARD_NUMBER_AMEX) });

                editTxtCardNumber.removeTextChangedListener(this);
                int cursorPosition = editTxtCardNumber.getSelectionStart();
                String withSpaces = formatTextAmEx(editable);
                editTxtCardNumber.setText(withSpaces);
                editTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));

                if (spaceDeleted) {
                    editTxtCardNumber.setSelection(editTxtCardNumber.getSelectionStart() - 1);
                    spaceDeleted = false;
                }

                editTxtCardNumber.addTextChangedListener(this);
            } else if(editTxtCardNumber.getText().length() > 0 
                    && (editTxtCardNumber.getText().charAt(0) == '4' || editTxtCardNumber.getText().charAt(0) == '5')) {
                editTxtCardNumber.setFilters(new InputFilter[] { new InputFilter.LengthFilter(Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) });

                editTxtCardNumber.removeTextChangedListener(this);
                int cursorPosition = editTxtCardNumber.getSelectionStart();
                String withSpaces = formatTextVisaMasterCard(editable);
                editTxtCardNumber.setText(withSpaces);
                editTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));

                if (spaceDeleted) {
                    editTxtCardNumber.setSelection(editTxtCardNumber.getSelectionStart() - 1);
                    spaceDeleted = false;
                }

                editTxtCardNumber.addTextChangedListener(this);
            } else {
                editTxtCardNumber.setFilters(new InputFilter[] { new InputFilter.LengthFilter(Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) });

                editTxtCardNumber.removeTextChangedListener(this);
                int cursorPosition = editTxtCardNumber.getSelectionStart();
                String withSpaces = formatTextVisaMasterCard(editable);
                editTxtCardNumber.setText(withSpaces);
                editTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));

                if (spaceDeleted) {
                    editTxtCardNumber.setSelection(editTxtCardNumber.getSelectionStart() - 1);
                    spaceDeleted = false;
                }

                editTxtCardNumber.addTextChangedListener(this);
            }
        }
    });

    private String formatTextVisaMasterCard(CharSequence text)
    {
        StringBuilder formatted = new StringBuilder();
        int count = 0;
        for (int i = 0; i < text.length(); ++i)
        {
            if (Character.isDigit(text.charAt(i)))
            {
                if (count % 4 == 0 && count > 0)
                    formatted.append(" ");
                formatted.append(text.charAt(i));
                ++count;
            }
        }
        return formatted.toString();
    }

    private String formatTextAmEx(CharSequence text)
    {
        StringBuilder formatted = new StringBuilder();
        int count = 0;
        for (int i = 0; i < text.length(); ++i)
        {
            if (Character.isDigit(text.charAt(i)))
            {
                if (count > 0 && ((count == 4) || (count == 10))) {
                    formatted.append(" ");
                }
                formatted.append(text.charAt(i));
                ++count;
            }
        }
        return formatted.toString();
    }

Other than formatting spaces, I also applied checks to make sure that card number doesn't exceed their maximum limit and user gets notified that he has entered all the digits by performing a change in font when the maximum limit is reached. Here is the function to perform the above mentioned operation.

除了格式化空格之外,我还应用了检查以确保卡号不超过其最大限制,并且当达到最大限制时,通过更改字体来通知用户他已输入所有数字。这是执行上述操作的函数。

public void checkCardNoEnteredCorrectly() {
if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '3') {
    if(editTxtCardNumber.getText().length() == Constants.MAX_LENGTH_CARD_NUMBER_AMEX) {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.amex), null, null, null);
    } else {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.amex), null, null, null);
    }
} else if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '4') {
    if(editTxtCardNumber.getText().length() == Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.visa), null, null, null);
    } else {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.visa), null, null, null);
    }
} else if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '5') {
    if(editTxtCardNumber.getText().length() == Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.master_card), null, null, null);
    } else {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.master_card), null, null, null);
    }
} else {
    editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.credit_card_number), null, null, null);
}

}

}

Note: The declarations made in Constants.java is as follows:

注意:Constants.java 中的声明如下:

public static final int MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD = 19;
public static final int MAX_LENGTH_CARD_NUMBER_AMEX = 17;

Hope it helps!

希望能帮助到你!

回答by Chirag

Please look at this project. Android form edit text is an extension of EditText that brings data validation facilities to the edittext

请看看这个项目。Android 表单编辑文本是 EditText 的扩展,它为编辑文本带来了数据验证功能