validar studio formato fecha android android-edittext mask textwatcher

validar - formato de fecha en android studio



Cómo enmascarar un EditText para mostrar el formato de fecha dd/mm/aaaa (5)

¿Cómo puedo formatear un EditText para seguir el formato " dd/mm/yyyy " de la misma manera que podemos formatear usando un TextWatcher para enmascarar la entrada del usuario para que parezca "0.05 €". No estoy hablando de limitar los caracteres o validar una fecha, simplemente enmascarar al formato anterior.


Escribí este TextWatcher para un proyecto, espero que sea útil para alguien. Tenga en cuenta que no valida la fecha ingresada por el usuario, y debe manejar eso cuando el foco cambie, ya que el usuario puede no haber terminado de ingresar la fecha.

Actualización 25/06 Hizo una wiki para ver si alcanzamos un mejor código final.

Actualización 07/06 Finalmente agregué algún tipo de validación al observador mismo. Hará lo siguiente con fechas inválidas:

  • Si el mes es mayor a 12, será 12 (diciembre)
  • Si la fecha es mayor que la del mes seleccionado, márcala como máximo para ese mes.
  • Si el año no está en el rango 1900-2100 , cámbielo para que esté dentro del rango

Esta validación se ajusta a mis necesidades, pero algunos de ustedes querrán cambiarla un poco, los rangos son fácilmente modificables y podría conectar estas validaciones al mensaje Toast , por ejemplo, para notificar al usuario que hemos modificado su fecha, ya que fue inválido

En este código, EditText que tenemos una referencia a nuestra date llamada EditText que tiene este TextWatcher adjunto, esto se puede hacer de la siguiente manera:

EditText date; date = (EditText)findViewById(R.id.whichdate); date.addTextChangedListener(tw);

TextWatcher tw = new TextWatcher() { private String current = ""; private String ddmmyyyy = "DDMMYYYY"; private Calendar cal = Calendar.getInstance();

Cuando el usuario cambia el texto de EditText

@Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (!s.toString().equals(current)) { String clean = s.toString().replaceAll("[^//d.]|//.", ""); String cleanC = current.replaceAll("[^//d.]|//.", ""); int cl = clean.length(); int sel = cl; for (int i = 2; i <= cl && i < 6; i += 2) { sel++; } //Fix for pressing delete next to a forward slash if (clean.equals(cleanC)) sel--; if (clean.length() < 8){ clean = clean + ddmmyyyy.substring(clean.length()); }else{ //This part makes sure that when we finish entering numbers //the date is correct, fixing it otherwise int day = Integer.parseInt(clean.substring(0,2)); int mon = Integer.parseInt(clean.substring(2,4)); int year = Integer.parseInt(clean.substring(4,8)); mon = mon < 1 ? 1 : mon > 12 ? 12 : mon; cal.set(Calendar.MONTH, mon-1); year = (year<1900)?1900:(year>2100)?2100:year; cal.set(Calendar.YEAR, year); // ^ first set year for the line below to work correctly //with leap years - otherwise, date e.g. 29/02/2012 //would be automatically corrected to 28/02/2012 day = (day > cal.getActualMaximum(Calendar.DATE))? cal.getActualMaximum(Calendar.DATE):day; clean = String.format("%02d%02d%02d",day, mon, year); } clean = String.format("%s/%s/%s", clean.substring(0, 2), clean.substring(2, 4), clean.substring(4, 8)); sel = sel < 0 ? 0 : sel; current = clean; date.setText(current); date.setSelection(sel < current.length() ? sel : current.length()); } }

También implementamos las otras dos funciones porque tenemos que

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

Esto produce el siguiente efecto, donde borrar o insertar caracteres revelará u ocultará la máscara dd/mm/yyyy . Debería ser fácil de modificar para adaptarse a otras máscaras de formato ya que traté de dejar el código lo más simple posible.


Esta respuesta no aplica una máscara completa para los dígitos restantes sin tipo. Sin embargo, está relacionado y es la solución que necesitaba. Funciona de manera similar a cómo funciona PhoneNumberFormattingTextWatcher .

A medida que escribe, agrega barras para separar una fecha formateada como mm/dd/yyyy . No hace ninguna validación, solo formateo.

No hay necesidad de una referencia de EditText . Solo configura al oyente y funciona. myEditText.addTextChangedListener(new DateTextWatcher());

import android.text.Editable; import android.text.TextWatcher; import java.util.Locale; /** * Adds slashes to a date so that it matches mm/dd/yyyy. * * Created by Mark Miller on 12/4/17. */ public class DateTextWatcher implements TextWatcher { public static final int MAX_FORMAT_LENGTH = 8; public static final int MIN_FORMAT_LENGTH = 3; private String updatedText; private boolean editing; @Override public void beforeTextChanged(CharSequence charSequence, int start, int before, int count) { } @Override public void onTextChanged(CharSequence text, int start, int before, int count) { if (text.toString().equals(updatedText) || editing) return; String digitsOnly = text.toString().replaceAll("//D", ""); int digitLen = digitsOnly.length(); if (digitLen < MIN_FORMAT_LENGTH || digitLen > MAX_FORMAT_LENGTH) { updatedText = digitsOnly; return; } if (digitLen <= 4) { String month = digitsOnly.substring(0, 2); String day = digitsOnly.substring(2); updatedText = String.format(Locale.US, "%s/%s", month, day); } else { String month = digitsOnly.substring(0, 2); String day = digitsOnly.substring(2, 4); String year = digitsOnly.substring(4); updatedText = String.format(Locale.US, "%s/%s/%s", month, day, year); } } @Override public void afterTextChanged(Editable editable) { if (editing) return; editing = true; editable.clear(); editable.insert(0, updatedText); editing = false; } }


Intente utilizar una biblioteca que resuelva este problema, ya que el enmascarado no está disponible de fábrica. Hay muchos casos de esquina (como agregar / eliminar caracteres en el medio del texto ya enmascarado) y para manejarlo correctamente terminarás con un montón de código (y errores).

Aquí hay algunas bibliotecas disponibles:
https://github.com/egslava/edittext-mask
https://github.com/dimitar-zabaznoski/MaskedEditText
https://github.com/pinball83/Masked-Edittext
https://github.com/RedMadRobot/input-mask-android
https://github.com/santalu/mask-edittext

** Tenga en cuenta que al momento de escribir estas bibliotecas no están exentas de problemas, por lo que es su responsabilidad elegir cuál le queda mejor y probar el código.


La respuesta actual es muy buena y me ayudó a guiarme hacia mi propia solución. Hay algunas razones por las que decidí publicar mi propia solución a pesar de que esta pregunta ya tiene una respuesta válida:

  • Estoy trabajando en Kotlin, no en Java. Las personas que se encuentran con el mismo problema tendrán que traducir la solución actual.
  • Quería escribir una respuesta que fuera más legible para que las personas puedan adaptarla más fácilmente a sus propios problemas.
  • Como lo sugirió dengue8830, encapsulé la solución a este problema en una clase, para que cualquiera pueda usarla sin siquiera preocuparse por la implementación.

Para usarlo, simplemente haz algo como:

  • DateInputMask (mEditText) .listen ()

Y la solución se muestra a continuación:

class DateInputMask(val input : EditText) { fun listen() { input.addTextChangedListener(mDateEntryWatcher) } private val mDateEntryWatcher = object : TextWatcher { var edited = false val dividerCharacter = "/" override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { if (edited) { edited = false return } var working = getEditText() working = manageDateDivider(working, 2, start, before) working = manageDateDivider(working, 5, start, before) edited = true input.setText(working) input.setSelection(input.text.length) } private fun manageDateDivider(working: String, position : Int, start: Int, before: Int) : String{ if (working.length == position) { return if (before <= position && start < position) working + dividerCharacter else working.dropLast(1) } return working } private fun getEditText() : String { return if (input.text.length >= 10) input.text.toString().substring(0,10) else input.text.toString() } override fun afterTextChanged(s: Editable) {} override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {} } }


una forma más limpia de usar el código de Juan Cortés es ponerlo en una clase:

public class DateInputMask implements TextWatcher { private String current = ""; private String ddmmyyyy = "DDMMYYYY"; private Calendar cal = Calendar.getInstance(); private EditText input; public DateInputMask(EditText input) { this.input = input; this.input.addTextChangedListener(this); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (s.toString().equals(current)) { return; } String clean = s.toString().replaceAll("[^//d.]|//.", ""); String cleanC = current.replaceAll("[^//d.]|//.", ""); int cl = clean.length(); int sel = cl; for (int i = 2; i <= cl && i < 6; i += 2) { sel++; } //Fix for pressing delete next to a forward slash if (clean.equals(cleanC)) sel--; if (clean.length() < 8){ clean = clean + ddmmyyyy.substring(clean.length()); }else{ //This part makes sure that when we finish entering numbers //the date is correct, fixing it otherwise int day = Integer.parseInt(clean.substring(0,2)); int mon = Integer.parseInt(clean.substring(2,4)); int year = Integer.parseInt(clean.substring(4,8)); mon = mon < 1 ? 1 : mon > 12 ? 12 : mon; cal.set(Calendar.MONTH, mon-1); year = (year<1900)?1900:(year>2100)?2100:year; cal.set(Calendar.YEAR, year); // ^ first set year for the line below to work correctly //with leap years - otherwise, date e.g. 29/02/2012 //would be automatically corrected to 28/02/2012 day = (day > cal.getActualMaximum(Calendar.DATE))? cal.getActualMaximum(Calendar.DATE):day; clean = String.format("%02d%02d%02d",day, mon, year); } clean = String.format("%s/%s/%s", clean.substring(0, 2), clean.substring(2, 4), clean.substring(4, 8)); sel = sel < 0 ? 0 : sel; current = clean; input.setText(current); input.setSelection(sel < current.length() ? sel : current.length()); } @Override public void afterTextChanged(Editable s) { } }

entonces puedes reutilizarlo

new DateInputMask(myEditTextInstance);