Android Date Masking for EditText (ADME)

To format an EditText so that the input pattern follows "dd/mm/yyyy", you can use a TextWatcher to monitor text changes and apply the desired format. Here's a TextWatcher I developed for a project. Keep in mind that this does not validate the user's input date; you should handle validation, for instance, when the focus changes since the user may not have finished entering the date.

Update Log

June 25: Converted it into a wiki to collaborate on refining the final code.

June 7: Added some validation to the TextWatcher. This now handles invalid dates as follows:

  • If the month is greater than 12, it sets the month to 12 (December).
  • If the day is greater than the maximum allowed for that month, it adjusts it to the month’s maximum.
  • If the year is outside the range 1900-2100, it adjusts to stay within this range.
The validation meets my needs, but you may want to customize it. The range can easily be modified, and you could connect this validation with a Toast message to inform the user when an invalid date has been adjusted.

In this example, I'm assuming we have a reference to our EditText called date with the TextWatcher applied:

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

Here's the implementation of the TextWatcher:
 
TextWatcher tw = new TextWatcher() {
    private String current = "";
    private String ddmmyyyy = "DDMMYYYY";
    private Calendar cal = Calendar.getInstance();

and then,

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

This creates the effect where inserting or deleting characters will show or hide the "dd/mm/yyyy" mask. You should be able to modify this easily to fit other mask formats, as I kept the code as simple as possible.

Appendix: Third-Party Library

This library simplifies date entry from users, allowing for date entry as text with verification based on date format, divider characters, and minimum or maximum dates with other options.


Post a Comment

Previous Next

نموذج الاتصال