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

300
23

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


4digit 4digit 4digit 4digit 

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

спросил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
1
Решение
384

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

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


Caveat


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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
304

Demo - How this works

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

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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
193

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


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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
150

Вот более чистое решение, использующее регулярные выражения. Хотя регулярные выражения могут быть неэффективными, в этом случае их будет достаточно, так как он обрабатывает строку длиной не более 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 already
// there before setting
if (!initial.equals(processed)) {
// set the value
s.replace(0, initial.length(), processed);
}

}

});

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
143

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


Чтобы разрешить редактирование в любом месте строки, а также для сохранения положения курсора, 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, 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;
}
}

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
106

Я только что сделал следующую реализацию и хорошо работал у меня, даже при вставке и вводе нового текста в любом месте 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);
}
}

}


Использование: editText.addTextChangedListener(new CreditCardFormattingTextWatcher());

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
106

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


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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
87

Пожалуйста, просмотрите проект . Текст редактирования формы Android - это расширение EditText, которое предоставляет средства проверки данных для edittext

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
87

Не уверен, что 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


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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
76

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


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


Кроме пробелов форматирования, я также применял проверки, чтобы убедиться, что номер карты не превышает их максимального предела, и пользователь получает уведомление о том, что он ввел все цифры, выполнив изменение шрифта, когда достигнут максимальный предел. Ниже приведена функция для выполнения вышеупомянутой операции.


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

}


Примечание. Декларации, сделанные в Constants.java, следующие:


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

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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
62

Здесь простое и легко настраиваемое решение с использованием класса 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. i.e. (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 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;
}

}

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
62

Я думаю, что мое решение может хорошо работать с любой текстовой операцией или операцией копирования-вставки.

Пожалуйста, смотрите код, как показано ниже,

  class BankNumberTextWatcher implements TextWatcher {
private int previousCodeLen = 0;

@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) {
if (s.length() > 0) {
String numbersOnly = s.toString().replaceAll("[^0-9]", "");
// current code pattern miss-match, then handle cursor position and format the code
handleEditInput(numbersOnly);
} else {
previousCodeLen = 0;
}
}

/**
* Handle EditText input process for credit card including insert, delete during middle position,
* end position or copy-paste controller
*
* @param numbersOnly the pure number without non-digital characters
*/
private void handleEditInput(final String numbersOnly) {
String code = formatNumbersAsCode(numbersOnly);
int cursorStart = etBankCardNumber.getSelectionStart();
etBankCardNumber.removeTextChangedListener(this);
etBankCardNumber.setText(code);
int codeLen = code.length();
if (cursorStart != codeLen) {
// middle-string operation
if (cursorStart > 0 && cursorStart % 5 == 0) {
if (codeLen > previousCodeLen) {
// insert, move cursor to next
cursorStart++;
} else if (codeLen < previousCodeLen) {
// delete, move cursor to previous
cursorStart--;
}
}
etBankCardNumber.setSelection(cursorStart);
} else {
// end-string operation
etBankCardNumber.setSelection(codeLen);
}
etBankCardNumber.addTextChangedListener(this);
previousCodeLen = codeLen;
}

/**
* formats credit code like 1234 1234 5123 1234
*
* @param s
* @return
*/
public String formatNumbersAsCode(CharSequence s) {
if (TextUtils.isEmpty(s)) {
return "";
}
int len = s.length();
StringBuilder tmp = new StringBuilder();
for (int i = 0; i < len; ++i) {
tmp.append(s.charAt(i));
if ((i + 1) % 4 == 0 && (i + 1) != len) {
tmp.append(" ");
}
}
return tmp.toString();
}
}

Делает inputType для нумерации EditText, чтобы избежать других символов в файле макета.

Надеюсь, что это будет полезно для вас.

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
62

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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
62

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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
62

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


}

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
62

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


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


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


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


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


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


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


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

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
44

Ни один из приведенных выше ответов не подходит для меня. Я создал один, который решает проблемы начала-конца/конца-строки/середины строки. Копирование и вставка также должны работать нормально. Это поддерживает Mastercard, Visa и Amex. Вы можете изменить разделитель. Если вам не нужен способ оплаты, просто удалите его. Это же Котлин. Идея проста. Каждый раз, когда текст изменился, я удаляю все разделители и заново добавляю их на основе формата. Решает проблему с начальными и средними строками. Тогда единственная проблема заключается в том, что вам нужно определить правильную позицию текста после добавления разделителей.

fun addCreditCardNumberTxtWatcher(et: EditText, separator: Char, paymentMethodType: PaymentMethodType): TextWatcher {
val tw = object : TextWatcher {
var mBlock = false
override fun afterTextChanged(s: Editable) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
Logger.d("_debug", "s: $s, start: $start, count: $count, after $after")
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if (mBlock)
return
var lastPos = et.selectionStart
val oldStr = et.text.toString().replace(separator.toString(), "", false)
var newFormattedStr = ""
if (before > 0) {
if (lastPos > 0 && et.text.toString()[lastPos - 1] == separator) lastPos--
}
Logger.d("_debug", "lastPos: $lastPos, s: $s, start: $start, before: $before, count $count")
mBlock = true
oldStr.forEachIndexed { i, c ->
when (paymentMethodType) {
PaymentMethodType.MASTERCARD, PaymentMethodType.VISA -> {
if (i > 0 && i % 4 == 0) {
newFormattedStr += separator
}
}
PaymentMethodType.AMERICAN_EXPRESS -> {
if (i == 4 || i == 10 || i == 15) {
newFormattedStr += separator
}
}
}
newFormattedStr += c
}
et.setText(newFormattedStr)
if (before == 0) {
if (et.text.toString()[lastPos - 1] == separator) lastPos++
}
et.setSelection(lastPos)
mBlock = false
}
}
et.addTextChangedListener(tw)
return tw
}

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
44

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

}

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
44

 private class TextWatcherIBAN implements TextWatcher {

@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) {
textInputEditText.removeTextChangedListener(this);
formatIBANEditText(textInputEditText);
textInputEditText.addTextChangedListener(this);

}
}

public void formatIBANEditText(TextInputEditText editText) {
String decimalAmount = editText.getText().toString();
int selection = editText.getSelectionEnd() == decimalAmount.length() ? -1 : editText.getSelectionEnd();
decimalAmount = formatIBAN(decimalAmount);
editText.setText(decimalAmount);

if (selection != -1) {
editText.setSelection(selection);
} else {
editText.setSelection(decimalAmount.length());
}

}

public String formatIBAN(String text) {
return formatterIBAN(new StringBuilder(text));
}

private String formatterIBAN(StringBuilder text) {
int group = text.toString().length() / 5;
int spaceCount = getSpaceCount(text);
if (spaceCount < group) {
return formatterIBAN(text.insert(4 + 5 * spaceCount, space));
} else {
return text.toString();
}
}

private int getSpaceCount(StringBuilder text) {
int spaceCount = 0;
for (int index = 0; index < text.length(); index++) {
if (text.charAt(index) == space.charAt(0)) {
spaceCount++;
}
}
return spaceCount;
}

textInputEditText.addTextChangedListener(new TextWatcherIBAN());

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
44

В вашем макете:

    <android.support.design.widget.TextInputEditText
android:id="@+id/et_credit_card_number"
android:digits=" 1234567890"
android:inputType="number"
android:maxLength="19"/>

Здесь TextWachter который устанавливает пробел на каждые 4 цифры в кредитной карте из 16 номеров.

class CreditCardFormatWatcher : TextWatcherAdapter() {

override fun afterTextChanged(s: Editable?) {
if (s == null || s.isEmpty()) return

s.forEachIndexed { index, c ->
val spaceIndex = index == 4 || index == 9 || index == 14
when {
!spaceIndex && !c.isDigit() -> s.delete(index, index + 1)
spaceIndex && !c.isWhitespace() -> s.insert(index, " ")
}
}

if (s.last().isWhitespace())
s.delete(s.length - 1, s.length)
}

}

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
45

Это решение было реализовано для IBAN, но принцип тот же, я попытался исправить все основные проблемы в ответах выше, если вы обнаружите ошибку, не стесняйтесь сказать это, спасибо.

Установите EditText и ограничьте символы, которые можно использовать:

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

Это 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();
}
}

ответил(а) 2021-01-19T17:11:04+03:00 2 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема