Форматировать кредитную карту в текстовом редакторе в android

Как сделать EditText принимать ввод в формате:

 4digit 4digit 4digit 4digit 

Я попробовал Пользовательский формат редактировать текст ввода android принять номер кредитной карты , но, к сожалению, я не смог удалить пробелы. Всякий раз, когда есть пробел, я не мог его удалить. Пожалуйста, помогите мне в выяснении проблемы.

После поиска нескольких ответов, которые являются «ОК». Я перешел к лучшему TextWatcher, который предназначен для правильной работы и независимо от 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)); } } } } 

Затем добавьте его в свой TextView, как и любой другой TextWatcher .

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

Это автоматически удалит пространство, разумно возвращающееся, чтобы пользователь мог на самом деле делать меньше нажатий клавиш при редактировании.

Предостережение

Если вы используете inputType="numberDigit" это отключит символы '-' и '', поэтому я рекомендую использовать inputType="phone" . Это позволяет использовать другие символы, но просто используйте собственный входной фильтр и проблему.

Демо - как это работает

Пример на 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; } }); 

Это отлично работает с редактированием начальной строки / конечной строки / средней строки, а также прекрасно работает.

Я изменил ответ Криса Дженкинса, чтобы сделать его более надежным. При этом, даже если пользователь редактирует середину текста, символы пробела по-прежнему вставлены (и автоматически удалены в неправильных местах) правильно.

Чтобы сделать эту работу правильно, убедитесь, что атрибуты 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; } } }); 

Я добавляю свое решение в список. Насколько мне известно, у него нет недостатка; Вы можете редактировать в середине, удалять символы пробела, копировать и вставлять в него и т. Д.

Чтобы разрешить редактирование в любом месте строки, а также для сохранения положения курсора, редактирование перемещается, и все пробелы (если они есть) выводятся один за другим. Затем добавляются новые пробелы в соответствующих положениях. Это гарантирует, что курсор перемещается вместе с изменениями, внесенными в содержимое.

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

Вот более чистое решение, использующее регулярные выражения. Хотя регулярные выражения могут быть неэффективными, в этом случае их будет достаточно, так как он обрабатывает строку длиной не более 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)", "$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); } } }); 

Эта реализация обеспечивает правильное размещение интервальных символов, даже если пользователь редактирует среднюю строку. Также поддерживаются другие символы, отображаемые на мягкой клавиатуре (например, тире); То есть пользователь не может их вводить. Одно из достижений, которое можно было бы сделать: эта реализация не позволяет исключить промежуточные символы в середине строки.

 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 .

Файл Gist

 /** * 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, которое предоставляет средства проверки данных для edittext

Не уверен, что TextWatcher подходит для использования – мы должны использовать InputFilter

Согласно документации Android, TextWatcher следует использовать для внешнего примера использования: один [EditView] для ввода пароля + один вид [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 } } 

И объедините с фильтром длины (Android SDK):

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

Это обрабатывает случай при наборе и удалении цифры.

(!) Но это не относится к случаю для копирования / вставки всей строки, это нужно сделать в другом классе InputFilter

Надеюсь, поможет !

Возможно, вы уже поняли это, но вот что я сделал. Единственным методом, который мне пришлось переопределить, был AfterTextChanged.

Проверьте, действительно ли форма кредитной карты действительна, базовый код для предотвращения бесконечной рекурсии

Если форма недействительна, удалите все пробелы и скопируйте их в другую строку, вставив там, где это необходимо, пробел.

Затем просто замените редактируемый новой строкой.

Если вам нужен код для определенного шага, не стесняйтесь спрашивать.

И Preethi, причина, по которой вы не можете удалить пробелы, заключается в том, что вы не можете изменить текст в обратном вызове onTextChanged. С сайта разработчика:

Public abstract void onTextChanged (CharSequence s, int start, int before, int count) Добавлен в уровень API 1

Этот метод вызывается, чтобы уведомить вас о том, что в течение s символы count, начинающиеся с момента запуска, только что заменили старый текст, длина которого ранее. Ошибка при попытке внести изменения в s из этого обратного вызова.

Вот пример, который использует всю функцию для принятия решения. Код может быть немного длиннее, но он будет быстрее, поскольку в основном используется функция заданных значений (start, before, count …). В этом примере добавьте «-» каждые 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. } } 

}

Вот мое решение. Мои комментарии должны содержать достаточно информации для разработчика 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; } } } 
 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) { } }); } 

Вот простое и легко настраиваемое решение, использующее класс TextWatcher . Он может быть назначен вашему EditText с помощью addTextChangedListener() .

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

После многого поиска и не получая удовлетворительного ответа для удовлетворения моих потребностей, я в конечном итоге написал свою собственную функцию.

Ниже приведен пример форматирования введенных данных кредитной карты в зависимости от типа вводимой карты. В настоящее время он заботится о 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:

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