How to mask an EditText to show the dd/mm/yyyy date format
I wrote this TextWatcher
for a project, hopefully it will be helpful to someone. Note that it does not validate the date entered by the user, and you should handle that when the focus changes, since the user may not have finished entering the date.
Update 25/06 Made it a wiki to see if we reach a better final code.
Update 07/06
I finally added some sort of validation to the watcher itself. It will do the following with invalid dates:
- If the month is greater than 12, it will be 12 (December)
- If the date is greater than the one for the month selected, make it the max for that month.
- If the year is not in the range
1900-2100
, change it to be in the range
This validation fits my needs, but some of you may want to change it a little bit, ranges are easily changeable and you could hook this validations to Toast
message for instance, to notify the user that we've modified his/her date since it was invalid.
In this code, I will be assuming that we have a reference to our EditText
called date
that has this TextWatcher
attached to it, this can be done something like this:
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();
When user changes text of the 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());
}
}
We also implement the other two functions because we have to
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) {}
};
This produces the following effect, where deleting or inserting characters will reveal or hide the dd/mm/yyyy
mask. It should be easy to modify to fit other format masks since I tried to leave the code as simple as possible.
Put a slash in the date of a EditText
Thanks you, after some days i got the solution, this class:
public class EditMMYY extends AppCompatEditText implements TextWatcher
{
private String sPrev = "";
private int iMon = 0;
private int iYear = 0;
private void InitValue()
{
setInputType(InputType.TYPE_CLASS_NUMBER);
setFilters(new InputFilter[] {new InputFilter.LengthFilter(5)});
setHint("MM/YY");
}
public EditMMYY(Context context)
{
super(context);
InitValue();
}
public EditMMYY(Context context, AttributeSet attrs)
{
super(context, attrs);
InitValue();
}
public EditMMYY(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
InitValue();
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count)
{
// Chequeo que el ingreso sea MM/YY
String sNew = s.toString();
int newLen = sNew.length();
if(sNew.equals(sPrev))
{
return;
}
switch(newLen)
{
case 0:
iMon = 0;
iYear = 0;
sPrev = sNew;
break;
case 1:
iMon = Integer.parseInt(sNew);
iYear = 0;
if(sPrev.length() == 0 && iMon > 1)
{ // Si se escribe un número mayor que 1, lo tomo como mes
sPrev = String.format("%02d/", iMon);
}
else
{
sPrev = sNew;
}
break;
case 2:
iMon = Integer.parseInt(sNew);
iYear = 0;
if(sPrev.length() == 1)
{
// Si ya es un mes válido, lo completo, sino dejo
// sPrev sin cambios hasta que se ingrese algo válido
if(iMon >= 1 && iMon <= 12)
{
sPrev = String.format("%02d/", iMon);
}
}
else
{
sPrev = sNew;
}
break;
case 3:
iMon = Integer.parseInt(sNew.substring(0, 2));
iYear = 0;
if(sPrev.length() == 2)
{
iMon = Integer.parseInt(sNew.substring(0, 2));
iYear = Integer.parseInt(sNew.substring(2, 3));
sPrev = String.format("%02d/%d", iMon, iYear);
}
else
{
sPrev = sNew;
}
break;
case 4:
case 5:
iMon = Integer.parseInt(sNew.substring(0, 2));
iYear = Integer.parseInt(sNew.substring(3, newLen));
sPrev = sNew;
break;
default:
sPrev = sNew;
break;
}
setText(sPrev);
setSelection(sPrev.length());
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after)
{
}
@Override
public void afterTextChanged(Editable s)
{
}
public int getMon()
{
return iMon;
}
public int getYear()
{
return iYear;
}
}
How to have a specific format for an EditText (dd/mm/yyyy)
just add this to your Oncreate
et5.addTextChangedListener(new TextWatcher(){
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
// TODO Auto-generated method stub
//Do Nothing
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
// TODO Auto-generated method stub
//Do Nothing
}
@Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
ss =input_Dob.getText().toString();
int o = 0;
if ((ss.charAt(2) == '/') && (ss.charAt(4) == '/')) {
Toast.makeText(Create_An_Account.this, "Format Is right", Toast.LENGTH_LONG).show();
} else {
tv5.setTextColor(Color.RED);
tv5.setText("Invalid Format");
}
ss = "";
}
});
Edittext - Mask with date format
Library author here.input-mask-android
is about text formatting, but your question looks more layout-related.
Three EditText
components plus two /
labels between them might do the trick. setOnEditorActionListener
and TextWatcher
listeners will help with cursor movement.
Or you could just put a couple of spaces within curly brackets and call it a day: [00]{ / }[00]{ / }[9900]
I'd also suggest reading more about our affine masks, and then use a couple of patterns for the sake of year correctness:
[00]{ / }[00]{ / }[00]
[00]{ / }[00]{ / }[0000]
How to allow mm/yyyy format in edittext for entering credit/debit card details
As by my understanding, your date divider must always be at position 2
private static final int CARD_DATE_DIVIDER_POSITION = CARD_DATE_DIVIDER_MODULO - 1; // means divider position is every 2nd symbol beginning with 0
with the above declaration, before appending the date divider, I should test this way ( you can change your isInputCorrect
method this way):
private boolean isInputCorrect(Editable s, int size, int dividerPosition, char divider) {
boolean isCorrect = s.length() <= size;
for (int i = 0; i < s.length(); i++) {
if (i > 0 && (i + 1) % dividerPosition == 0) {
if (divider=='/') {
if (i==2) {
isCorrect &= divider == s.charAt(i);
}
}
} else {
isCorrect &= Character.isDigit(s.charAt(i));
}
}
return isCorrect;
}
And then after the text has changed can be modified a bit to become:
@Override
public void afterTextChanged(Editable s) {
card_num = String.valueOf(s);
String ss=cardDateEditText.getText().toString();
Log.i("TAG", "Card Date:" + s);
Log.i("TAG", "Card Date :" + ss);
if (s == cardno.getEditableText()) {
if (!isInputCorrect(s, CARD_NUMBER_TOTAL_SYMBOLS, CARD_NUMBER_DIVIDER_MODULO, CARD_NUMBER_DIVIDER)) {
s.replace(0, s.length(), concatString(getDigitArray(s, CARD_NUMBER_TOTAL_DIGITS), CARD_NUMBER_DIVIDER_POSITION, CARD_NUMBER_DIVIDER));
}
} else if (s == cardDateEditText.getEditableText()) {
if (s.length()<=CARD_DATE_TOTAL_SYMBOLS)
{
if (!isInputCorrect(s, CARD_DATE_TOTAL_SYMBOLS, CARD_DATE_DIVIDER_MODULO, CARD_DATE_DIVIDER)) {
Log.e("TAG",s+"");
s.replace(0, s.length(), concatString(getDigitArray(s, CARD_DATE_TOTAL_DIGITS), CARD_DATE_DIVIDER_POSITION, CARD_DATE_DIVIDER));
}
}else
{
s.delete(CARD_DATE_TOTAL_SYMBOLS,s.length());
}
} else if (s == cardCVCEditText.getEditableText()) {
if (s.length() > CARD_CVC_TOTAL_SYMBOLS) {
s.delete(CARD_CVC_TOTAL_SYMBOLS, s.length());
}
}
}
So you test also the length of the editable text.
The date divider must be included only once and just after the month represented by two first characters. That means the last digit must be at 2nd position index 1, at that instant i==1
. So no other divider will be appended. I should have to test if the divider set is for date, to know I'm dealing with the date and then test if the index is not greater or equals to 2.
That's how I could handle that. I hope this will help.
Is it possible to split EditText with / to create a date?
You could add the date mask "##/##/##" in your EditText. Here is a tutorial on how to do this:
https://medium.com/@diegoy_kuri/masks-in-android-edit-text-fields-33a2fd47f1af
Date from EditText
getDay()
returns the day of week, so this is wrong. Use getDate()
.
getMonth()
starts at zero so you need to add 1 to it.
getYear()
returns a value that is the result of subtracting 1900 from the year so you need to add 1900 to it.
abcd
- well, you are explicitly adding that to the end of the string so no surprises there :)
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy");
Date myDate;
try {
myDate = df.parse(date);
String myText = myDate.getDate() + "-" + (myDate.getMonth() + 1) + "-" + (1900 + myDate.getYear());
Log.i(TAG, myText);
} catch (ParseException e) {
e.printStackTrace();
}
All of these are deprecated though and you should really use a Calendar
instead.
Edit: quick example of Calendar
Calendar cal = Calendar.getInstance();
cal.setTime(myDate);
cal.get(Calendar.DAY_OF_MONTH); // and so on
Related Topics
How to Check the Multiple Permission at Single Request in Android M
Android Gridview Row Dividers/Separators
Open Specific Activity When Notification Clicked in Fcm
Play Sound Using Soundpool Example
Proguard Ignores Config File of Library
Android Alarmmanager Not Waking Phone Up
How to Hide Android Soft Keyboard on Edittext
Oauth 2.0 Authorization for Linkedin in Android
What Is a Full Android Database Helper Class for an Existing SQLite Database
How to Scroll to Bottom in a Scrollview on Activity Startup
How to Check If Facebook Is Installed Android
Livedata Prevent Receive the Last Value When Start Observing
Httpurlconnection Worked Fine in Android 2.X But Not in 4.1: No Authentication Challenges Found
Android Intent.Getstringextra() Returns Null
Mediaplayer.Setdatasource Causes Ioexception for Valid File
How to Setlayoutparams() for an Imageview
How to Create an Animated Gif from Jpegs in Android (Development)