tarjeta samsung que puedo porque pasar para mover memoria las interno hacer descarguen como cambiar app aplicaciones almacenamiento ala android formatting android-edittext

android - samsung - mover aplicaciones a sd sin root



Formatear la tarjeta de crédito en editar texto en Android (19)

Acabo de hacer la siguiente implementación y funciona bien para mí, incluso pegando y escribiendo texto nuevo en cualquier posición de EditText .

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

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

Cómo hacer que EditText acepte entrada en formato:

4digit 4digit 4digit 4digit

Intenté el formato personalizado editar entrada de texto android para aceptar el número de tarjeta de crédito , pero lamentablemente no pude eliminar los espacios. Cada vez que hay un espacio, no pude eliminarlo. Por favor, ayúdame a descubrir el problema.


Aquí está mi solución. Mis comentarios deberían ser suficiente información para que un desarrollador de Android entienda lo que está pasando, pero si tiene alguna pregunta, no dude en preguntar y responderé a mi leal saber y entender.

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


Aquí hay un ejemplo que usa todas las funciones apropiadamente para tomar una decisión. El código puede ser un poco más largo, pero será más rápido ya que usa principalmente la función dada por los valores (inicio, antes, conteo ...). En este ejemplo, agregue "-" cada 4 dígitos y elimínelos también cuando el usuario use la tecla de retroceso. también, asegúrese de que el cursor esté al final.

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

}


Aquí hay una solución más limpia que usa expresiones regulares. Aunque las expresiones regulares pueden ser ineficientes, serían suficientes en este caso, ya que está procesando una cadena de como máximo 19 caracteres, incluso si el procesamiento se produce después de cada pulsación de tecla.

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


Después de buscar mucho y no obtener una respuesta satisfactoria para satisfacer mis necesidades, terminé escribiendo mi propia función.

Aquí hay un ejemplo para formatear los detalles ingresados ​​de la tarjeta de crédito según el tipo de tarjeta que se ingrese. Actualmente se ocupa de Visa, MasterCard y American Express con el propósito de formatear.

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

Además de los espacios de formato, también apliqué cheques para asegurarme de que el número de tarjeta no exceda su límite máximo y se notifica al usuario que ingresó todos los dígitos realizando un cambio en la fuente cuando se alcanza el límite máximo. Aquí está la función para realizar la operación mencionada anteriormente.

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

}

Nota: Las declaraciones hechas en Constants.java son las siguientes:

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

¡Espero eso ayude!


Después de encontrar respuestas múltiples que están ''OK''. Avancé hacia un TextWatcher mejor diseñado para funcionar correctamente e independientemente de TextView .

La clase TextWatcher es la siguiente:

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

Luego agréguelo a su TextView como lo haría con cualquier otro TextWatcher .

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

Esto eliminará automáticamente el espacio que se remonta sensiblemente para que el usuario pueda hacer menos teclas al editar.

Advertencia

Si está utilizando inputType="numberDigit" esto desactivará los caracteres ''-'' y '''', por lo que recomiendo usar, inputType="phone" . Esto permite otros caracteres, pero solo use un filtro de entrada personalizado y un problema resuelto.


En tu diseño:

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

Aquí el TextWachterque establece un espacio en cada 4 dígitos en una tarjeta de crédito de 16 números.

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


Esta implementación garantiza la colocación correcta de los caracteres espaciados, incluso si el usuario edita una línea media. También se admiten otros caracteres que aparecen en el teclado virtual (como el guión); es decir, el usuario no puede ingresarlos. Una mejora que se podría hacer: esta implementación no permite la eliminación de caracteres de espaciado a mitad de cadena.

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

Funciona bien con una implementación apropiada PasswordTransformationMethod para enmascarar los dígitos CC.


Estoy agregando mi solución a la lista. Por lo que yo sé, no tiene ningún inconveniente; puede editar en el medio, eliminar caracteres espaciados, copiar y pegar, etc.

Para permitir que la edición tenga lugar en cualquier lugar de la cadena, y para mantener la posición del cursor, se desplaza el editable y todos los espacios en blanco (si los hay) se eliminan uno por uno. Se añaden nuevos espacios en blanco en las posiciones apropiadas. Esto asegurará que el cursor se mueva junto con los cambios hechos a los contenidos.

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


Modifiqué la respuesta de Chris Jenkins para hacerlo más robusto. Con esto, incluso si el usuario edita el medio del texto, los caracteres de espaciado aún se insertan (y se eliminan automáticamente en lugares incorrectos) correctamente.

Para que esto funcione correctamente, asegúrese de que los atributos EditText estén establecidos de la siguiente manera (tenga en cuenta el espacio en digits ):

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

Entonces aquí está el TextWatcher que necesitas. La clase anónima también se puede convertir en estática, ya que es independiente de 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; } } });


No estoy seguro de que TextWatcher sea ​​lo correcto: debemos usar InputFilter

De acuerdo con la documentación de Android, TextWatcher se debe utilizar para un ejemplo de uso externo: uno [EditView] para la entrada de contraseña + una vista [TextView] que muestra "débil", "fuerte", etc.

Para el formato de tarjeta de crédito , estoy usando 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 } }

Y combine con un filtro de longitud (Android SDK):

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

Esto maneja el caso al escribir y eliminar un dígito.

(!) Pero esto no maneja el caso de una copia / pegado de una cadena completa, esta debería hacerse en una clase diferente de InputFilter

Espero eso ayude !


Por favor mira este project . El texto de edición de formularios de Android es una extensión de EditText que brinda servicios de validación de datos al texto de edición


Puede que ya lo hayas descifrado, pero esto es lo que hice. El único método que tuve que anular fue AfterTextChanged.

Compruebe si el formulario de la tarjeta de crédito ya es válido, caso base para evitar la recursión infinita

Si el formulario no es válido, elimine todos los espacios en blanco y cópielos en otra cadena, insertando espacios en blanco donde corresponda.

Luego simplemente reemplace el editable con su nueva cadena.

Si necesita un código para un paso en particular, siéntase libre de preguntar.

Y Preethi, la razón por la que no puede eliminar espacios es porque no puede cambiar el texto en la devolución de llamada onTextChanged. Desde el sitio del desarrollador:

public abstract void onTextChanged (CharSequence s, int start, int before, int count) Agregado en API nivel 1

Se llama a este método para notificarle que, dentro de s, los caracteres de recuento que empiezan al principio acaban de reemplazar el texto anterior que tenía una longitud anterior. Es un error intentar hacer cambios a s desde esta devolución de llamada.


Ejemplo en github.com

Última respuesta, pero creo que puede ser útil para alguien:

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

esto funciona perfectamente con la edición start-string / end-string / mid-string, también pegar funciona perfectamente.


Creo que mi solución puede funcionar bien sea cual sea la operación de texto intermedio o la operación de copiar y pegar.

Por favor vea el código como abajo,

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

Agrega inputType a number para EditText para evitar otros caracteres en el archivo de diseño.

Espero que sea útil para ti.


Esta solución se implementó para IBAN, pero el principio es el mismo, traté de corregir todos los problemas principales en las respuestas anteriores, si encuentra un error, siéntase libre de decirlo, gracias.

Establezca EditText y restrinja los caracteres que se pueden usar:

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

Este es el 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(); } }


Aquí hay una solución simple y fácilmente personalizable que usa la TextWatcherclase. Puede asignarse a su EditTextuso del addTextChangedListener()método.

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

Alternativamente, aquí hay una implementación mucho más limpia basada en implementación .

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


Ninguna de las respuestas anteriores es perfecta para mí. Creé uno que resuelve los problemas de la cadena de inicio / final / cadena media. Copiar y pegar también debería funcionar bien. Esto es compatible con Mastercard, Visa y Amex. Puedes cambiar el separador. Si no necesita el método de pago, simplemente elimínelo. Aunque es Kotlin. La idea es simple Cada vez que cambié el texto, eliminé todos los separadores y los volví a agregar en el formato. El problema resuelve el problema de cadena de inicio / cuerda intermedia. Entonces, el único problema es que necesita encontrar la posición de texto correcta después de agregar separadores.

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 }


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