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

如何使EditText接受格式input:

 4digit 4digit 4digit 4digit 

我试过自定义格式编辑文本inputandroid接受信用卡号码 ,但不幸的是我无法删除空格。 每当有一个空间,我不能删除它。 请帮我找出问题。

find多个“确定”的答案后。 我转向了一个更好的TextWatcher,它被devise为正确地独立于TextView

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)); } } } } 

然后像任何其他TextWatcher一样将其添加到TextView中。

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

这将自动删除空间明智地回来,所以用户实际上可以做更less的击键编辑。

警告

如果您使用的是inputType="numberDigit"则会禁用“ – ”和“”字符,所以我build议使用inputType="phone" 。 这使其他字符,但只是使用自定义inputfilter和问题解决。

演示 - 这是如何工作的

在github.com上的例子

迟到的答案,但我想这可能有助于某人:

  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(), buildCorrecntString(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++) { // chech 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 buildCorrecntString(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; } }); 

这可以完美的与开始string/结束string/中间string编辑,也粘贴完美的作品。

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

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

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

那么这里是你需要的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; } } }); 

这是使用正则expression式的更清洁的解决scheme 虽然正则expression式可能效率低下,但是在这种情况下,即使处理发生在每次按键之后,它也会处理最多19个字符的string。

 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)", "$1 "); // 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); } } }); 

我将我的解决scheme添加到列表中。 据我所知,它没有缺点; 你可以在中间编辑,删除空格字符,复制和粘贴等等。

为了允许在string中的任何位置进行编辑,并保持光标位置,将遍历Editable,并逐个取出所有空格(如果有的话)。 然后在适当的位置添加新的空白。 这将确保光标随着对内容的更改而移动。

 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, eg "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; } } 

即使用户编辑中间string,此实现也可确保间隔字符的正确放置。 软键盘上显示的其他字符(如短划线)也受支持; 也就是说,用户不能input它们。 一个可以改进的地方是:这个实现不允许在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)); } } } } } 

使用适当的PasswordTransformationMethod实现可以很好地屏蔽CC数字。

我只是做了下一个实现,并为我工作,即使在EditText任何位置粘贴和input新的文本。

要点文件

 /** * 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); } } } 

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

请看这个项目 。 Android表单编辑文本是EditText的扩展,它将数据validationfunction引入edittext

不确定TextWatcher是否正确,我们应该使用InputFilter

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

对于信用卡格式我使用的是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 } } 

结合长度filter(Android SDK):

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

这种情况下处理键入和删除数字的情况下。

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

希望能帮助到你 !

你可能已经知道了,但这是我做的。 我必须重写的唯一方法是AfterTextChanged。

检查信用卡的forms是否已经有效,以防止无限recursion

如果表单无效,请删除所有空格,并复制到另一个string中,在适当的位置插入空格。

然后简单地用新的stringreplace可编辑。

如果您需要特定步骤的代码,请随时询问。

而Preethi,你不能删除空格的原因是因为你不能在onTextChangedcallback中改变文本。 来自开发者网站:

public abstract void onTextChanged(CharSequence s,int start,int before,int count)在API级别1中添加

这个方法被调用来通知你,在s中,从开始处开始的计数字符刚刚replace了之前有长度的旧文本。 尝试从此callback中更改s是错误的。

 int keyDel; String a; String a0; int isAppent = 0; final String ch = " "; private void initListner() { txtCreditNumber.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { boolean flag = true; if (s.length() > 19) { txtCreditNumber.setText(a0); txtCreditNumber.setSelection(txtCreditNumber.getText().length()); return; } String eachBlock[] = s.toString().split(ch); for(int i = 0; i < eachBlock.length; i++) { if (eachBlock[i].length() > 4) { flag = false; } } if (a0.length() > s.toString().length()) { keyDel = 1; } if (flag) { if (keyDel == 0) { if (((txtCreditNumber.getText().length() + 1) % 5) == 0) { if (s.toString().split(ch).length <= 3) { isAppent = 1; txtCreditNumber.setText(s + ch); isAppent = 0; txtCreditNumber.setSelection(txtCreditNumber.getText().length()); a = txtCreditNumber.getText().toString(); return; } } if (isAppent == 0) { String str = s.toString(); if (str.lastIndexOf(ch) == str.length() - 1) { str = str.substring(0, str.lastIndexOf(ch)); keyDel = 1; txtCreditNumber.setText(str); keyDel = 0; txtCreditNumber.setSelection(txtCreditNumber.getText().length()); a = txtCreditNumber.getText().toString(); return; } } } else { String str = s.toString(); if (str.length() > 0 && str.lastIndexOf(ch) == str.length() - 1) { str = str.substring(0, str.lastIndexOf(ch)); keyDel = 1; txtCreditNumber.setText(str); keyDel = 0; txtCreditNumber.setSelection(txtCreditNumber.getText().length()); a = txtCreditNumber.getText().toString(); return; } else { a = txtCreditNumber.getText().toString(); keyDel = 0; } } } else { String str = s.toString(); str = str.substring(0, str.length() - 1) + ch + str.substring(str.length() - 1, str.length()); a = str; txtCreditNumber.setText(a); txtCreditNumber.setSelection(txtCreditNumber.getText().length()); } } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub a0 = s.toString(); } @Override public void afterTextChanged(Editable s) { } }); } 

这里是一个例子,使用所有的function来做出决定。 代码可能会更长一点,但它会更快,因为它主要使用函数给定值(开始,之前,计数…)。 这个例子每4位加上一个“ – ”,并且在用户使用退格的时候删除它们。 同样,确保光标将在最后。

 public class TextWatcherImplement implements TextWatcher { private EditText creditCard; private String beforeText, currentText; private boolean noAction, addStroke, dontAddChar, deleteStroke; public TextWatcherImplement(EditText creditCard) { // TODO Auto-generated constructor stub this.creditCard = creditCard; noAction = false; addStroke = false; dontAddChar = false; deleteStroke = false; } /* here I save the previous string if the max character had achieved */ @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub Log.i("TextWatcherImplement", "beforeTextChanged start==" + String.valueOf(start) + " count==" + String.valueOf(count) + " after==" + String.valueOf(after)); if (start >= 19) beforeText = s.toString(); } /* here I check were we add a character, or delete one. if we add character and it is time to add a stroke, then I flag it -> addStroke if we delete a character and it time to delete a stroke, I flag it -> deleteStroke if we are in max character for the credit card, don't add char -> dontAddChar */ @Override public void onTextChanged(CharSequence s, int start, int before, int count) { // TODO Auto-generated method stub Log.i("TextWatcherImplement", "onTextChanged start==" + String.valueOf(start) + " before==" + String.valueOf(before) + " count==" + String.valueOf(count) + " noAction ==" + String.valueOf(noAction)); if ( (before < count) && !noAction ) { if ( (start == 3) || (start == 8) || (start == 13) ) { currentText = s.toString(); addStroke = true; } else if (start >= 19) { currentText = s.toString(); dontAddChar = true; } } else { if ( (start == 4) || (start == 9) || (start == 14) ) { //(start == 5) || (start == 10) || (start == 15) currentText = s.toString(); deleteStroke = true; } } } /* noAction flag is when we change the text, the interface is being called again. the NoAction flag will prevent any action, and prevent a ongoing loop */ @Override public void afterTextChanged(Editable stext) { // TODO Auto-generated method stub if (addStroke) { Log.i("TextWatcherImplement", "afterTextChanged String == " + stext + " beforeText == " + beforeText + " currentText == " + currentText); noAction = true; addStroke = false; creditCard.setText(currentText + "-"); } else if (dontAddChar) { dontAddChar = false; noAction = true; creditCard.setText(beforeText); } else if (deleteStroke) { deleteStroke = false; noAction = true; currentText = currentText.substring(0, currentText.length() - 1); creditCard.setText(currentText); } else { noAction = false; creditCard.setSelection(creditCard.getText().length()); // set cursor at the end of the line. } } 

}

这是我的解决scheme。 我的意见应该足以让Android开发人员了解所发生的事情,但如果您有任何问题,请随时询问,我会尽我所能回答。

 private KeyEvent keyEvent; final TextWatcher cardNumberWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int start, int before, int count) { // NOT USING } @Override public void onTextChanged(CharSequence charSequence, int start, int before, int count) { // NOT USING } @Override public void afterTextChanged(Editable editable) { String cardNumbersOnly = editable.toString().replace("-", ""); /** * @PARAM keyEvent * This gets called upon deleting a character so you must keep a * flag to ensures this gets skipped during character deletion */ if (cardNumbersOnly.length() >= 4 && keyEvent == null) { formatCreditCardTextAndImage(this); } keyEvent = null; } }; cardNumberEditText.addTextChangedListener(cardNumberWatcher); /** * @LISTENER * Must keep track of when the backspace event has been fired to ensure * that the delimiter character and the character before it is deleted * consecutively to avoid the user from having to press backspace twice */ cardNumberEditText.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_UP) { // Hold reference of key event for checking within the text watcher keyEvent = event; String cardNumberString = cardNumberEditText.getText().toString(); if (keyCode == event.KEYCODE_DEL) { if (cardNumberString.substring(cardNumberString.length() - 1).equals("-")) { // Remove listener to avoid infinite looping cardNumberEditText.removeTextChangedListener(cardNumberWatcher); // Remove hyphen and character before it cardNumberEditText.setText(cardNumberString.substring(0, cardNumberString.length() - 1)); // Set the cursor back to the end of the text cardNumberEditText.setSelection(cardNumberEditText.getText().length()); // Add the listener back cardNumberEditText.addTextChangedListener(cardNumberWatcher); } else if (cardNumberString.length() < 2) { cardNumberBrandImageView.setImageDrawable(null); cardNumberBrandImageView.setVisibility(View.INVISIBLE); } } } return false; } }); } private void formatCreditCardTextAndImage (TextWatcher textWatcher) { // Remove to avoid infinite looping cardNumberEditText.removeTextChangedListener(textWatcher); String cardNumberString = cardNumberEditText.getText().toString(); /** * @CONDITION * Append delimiter after every fourth character excluding the 16th */ if ((cardNumberString.length() + 1) % 5 == 0 && !cardNumberString.substring(cardNumberString.length() - 1).equals("-")) { cardNumberEditText.setText(cardNumberString + "-"); } // Set the cursor back to the end of the text cardNumberEditText.setSelection(cardNumberEditText.getText().length()); cardNumberEditText.addTextChangedListener(textWatcher); /** * @CardBrand * Is an enum utility class that checks the card numbers * against regular expressions to determine the brand and updates the UI */ if (cardNumberString.length() == 2) { switch (CardBrand.detect(cardNumberEditText.getText().toString())) { case VISA: cardNumberBrandImageView.setImageResource(R.drawable.visa); cardNumberBrandImageView.setVisibility(View.VISIBLE); card.setBrand(Brand.Visa); break; case MASTERCARD: cardNumberBrandImageView.setImageResource(R.drawable.mastercard); cardNumberBrandImageView.setVisibility(View.VISIBLE); card.setBrand(Brand.MasterCard); break; case DISCOVER: cardNumberBrandImageView.setImageResource(R.drawable.discover); cardNumberBrandImageView.setVisibility(View.VISIBLE); card.setBrand(Brand.Discover); break; case AMERICAN_EXPRESS: cardNumberBrandImageView.setImageResource(R.drawable.americanexpress); cardNumberBrandImageView.setVisibility(View.VISIBLE); card.setBrand(Brand.AmericanExpress); break; case UNKNOWN: cardNumberBrandImageView.setImageDrawable(null); cardNumberBrandImageView.setVisibility(View.INVISIBLE); card.setBrand(null); break; } } } 

这是一个使用TextWatcher类的简单且易于定制的解决scheme。 可以使用addTextChangedListener()方法将其分配给EditText

 new TextWatcher() { /** Formats the Field to display user-friendly separation of the input values. */ @Override public final void afterTextChanged(final Editable pEditable) { // Declare the separator. final char lSeparator = '-'; // Declare the length of separated text. ie (XXXX-XXXX-XXXX) final int lSeparationSize = 4; // Declare the count; tracks the number of allowed characters in a row. int lCount = 0; // Iterate the Characters. for(int i = 0; i < pEditable.length(); i++) { // Fetch the current character. final char c = pEditable.charAt(i); // Is it a usual character. Here, we permit alphanumerics only. final boolean lIsExpected = (Character.isDigit(c) || Character.isLetter(c)) && (c != lSeparator); // Is the character expected? if(lIsExpected) { // Increase the count. lCount++; } else { // Is it a separator? if(c == lSeparator) { // Reset the count. lCount = 0; // Continue the iteration. continue; } } // Has the count been exceeded? Is there more text coming? if(lCount >= (lSeparationSize + 1) && (i < pEditable.length())) { // Reset the count. lCount = 0; // Insert the separator. pEditable.insert(i, Character.toString(lSeparator)); // Increase the iteration count. i++; } } } /** Unused overrides. */ @Override public final void beforeTextChanged(final CharSequence pCharSequence, final int pStart, final int pCount, final int pAfter) { } @Override public final void onTextChanged(final CharSequence pCharSequence, final int pStart, final int pBefore, final int pCount) { } } 

另外,这里是一个基于epool实现的更简洁的实现。

 public final class TextGroupFormattingListener implements TextWatcher { /* Member Variables. */ private final int mGroupLength; private final String mSeparator; private String mSource; /** Constructor. */ public TextGroupFormattingListener(final String pSeparator, final int pGroupLength) { // Initialize Member Variables. this.mSeparator = pSeparator; this.mGroupLength = pGroupLength; this.mSource = ""; } /** Formats the Field to display user-friendly separation of the input values. */ @Override public final void afterTextChanged(final Editable pEditable) { // Fetch the Source. String lSource = pEditable.toString(); // Has the text changed? if (!this.getSource().equals(lSource)) { // Remove all of the existing Separators. lSource = lSource.replace(this.getSeparator(), ""); // Allocate a StringBuilder. StringBuilder lStringBuilder = new StringBuilder(); // Iterate across the Source String, which contains the raw user input. for(int i = 0; i < lSource.length(); i++) { // Have we exceeded the GroupLength? if(i > 0 && i % this.getGroupLength() == 0) { // Append the separator. lStringBuilder.append(this.getSeparator()); } // Append the user's character data. lStringBuilder.append(lSource.charAt(i)); } // Track changes to the Source. this.setSource(lStringBuilder.toString()); // Replace the contents of the Editable with this new String. pEditable.replace(0, pEditable.length(), this.getSource()); } } /** Unused overrides. */ @Override public final void beforeTextChanged(final CharSequence pCharSequence, final int pStart, final int pCount, final int pAfter) { } @Override public final void onTextChanged(final CharSequence pCharSequence, final int pStart, final int pBefore, final int pCount) { } public final int getGroupLength() { return this.mGroupLength; } public final String getSeparator() { return this.mSeparator; } private final void setSource(final String pSource) { this.mSource = pSource; } private final String getSource() { return this.mSource; } } 

经过多次的search,没有得到满意的答案以满足我的需求,我终于写下了自己的function。

这里是一个例子,根据input的卡的types来格式化input的信用卡信息。 目前,Visa,万事达和美国运通为格式化。

  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:

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

希望能帮助到你!