12

I want to implement algorithm like this :

As soon as user starts entering numbers for "MM" , it should give "/" after the user enters two digits of "MM" and then , it should let only enter last two digits for "yy". How can I achieve this kind of functionality?

11 Answers11

14
android:maxLength="5"

And set as phone to editText so that it can display "/"

android:inputType="phone"

on the textWatcher do this .... I will validate later for the correct mm/yy month year to avoid users input data like 55/66 max should be 12/31 but it should be validated for months with less or 30 days...

 @Override
    public void afterTextChanged(Editable editable) {


        if (editable.length() > 0 && (editable.length() % 3) == 0) {
            final char c = editable.charAt(editable.length() - 1);
            if ('/' == c) {
                editable.delete(editable.length() - 1, editable.length());
            }
        }
        if (editable.length() > 0 && (editable.length() % 3) == 0) {
            char c = editable.charAt(editable.length() - 1);
            if (Character.isDigit(c) && TextUtils.split(editable.toString(), String.valueOf("/")).length <= 2) {
                editable.insert(editable.length() - 1, String.valueOf("/"));
            }
        }
    }
Goodlife
  • 3,822
  • 2
  • 24
  • 23
5

I just implement a method like Uber credit card date format with auto check, autocomplete, auto move to CVV edittext.

 <EditText
            android:id="@+id/credit_card_expire_et"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="@dimen/activity_horizontal_margin"
            android:layout_weight="1"
            android:hint="MM/YY"
            android:inputType="number"
            android:maxLength="5"
            android:maxLines="1" />

    <EditText
        android:id="@+id/credit_card_cvv_et"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_weight="1"
        android:hint="CVV"
        android:inputType="number"
        android:maxLength="3"
        android:maxLines="1" />

.

@BindView(R.id.credit_card_expire_et)
EditText creditExpireEt;
@BindView(R.id.credit_card_cvv_et)
EditText creditCVVEt;


@OnTextChanged(value = R.id.credit_card_expire_et, callback = BEFORE_TEXT_CHANGED)
    void beforeExpireEtChanged() {
        previousLength = creditExpireEt.getText().toString().length();
    }

    @OnTextChanged(R.id.credit_card_expire_et)
    void autoFixAndMoveToNext() {
        int length = creditExpireEt.getText().toString().trim().length();

        if (previousLength <= length && length < 3) {
            int month = Integer.parseInt(creditExpireEt.getText().toString());
            if (length == 1 && month >= 2) {
                String autoFixStr = "0" + month + "/";
                creditExpireEt.setText(autoFixStr);
                creditExpireEt.setSelection(3);
            } else if (length == 2 && month <= 12) {
                String autoFixStr = creditExpireEt.getText().toString() + "/";
                creditExpireEt.setText(autoFixStr);
                creditExpireEt.setSelection(3);
            } else if (length ==2 && month > 12) {
                creditExpireEt.setText("1");
                creditExpireEt.setSelection(1);
            }
        } else if (length == 5) {
            creditCVVEt.requestFocus(); // auto move to next edittext
        }
    }
Xianwei
  • 2,381
  • 2
  • 22
  • 26
4
EditText et_expiry_date= (EditText) findViewById(R.id.et_expiry_date);

et_expiry_date.addTextChangedListener(new 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) {
    String current = s.toString();
    if (current.length() == 2 && start == 1) {
            et_expiry_date.setText(current + "/");
            et_expiry_date.setSelection(current.length() + 1);
    } 
    else if (current.length() == 2 && before == 1) {
        current = current.substring(0, 1);
        et_expiry_date.setText(current);
        et_expiry_date.setSelection(current.length());
    }
}

@Override
public void afterTextChanged(Editable s) {}
});
Limo
  • 41
  • 1
4

add these on youredittext.addTextChangedListener

         @Override
         public void onTextChanged(CharSequence s, int start, int before, int count) {

            if (s.length() == 2) {
                if(start==2 && before==1 && !s.toString().contains("/")){
                    youredittext.setText(""+s.toString().charAt(0));
                    youredittext.setSelection(1);
                }
                else {
                    youredittext.setText(s + "/");
                    youredittext.setSelection(3);
                }
            }
          }
balaji
  • 41
  • 3
3

First set the max amount of characters of the EditText to 5, like this:

android:maxLength="5"

And set as numbers editText

android:inputType="number"

Then add an onEditTextChangedListener to the EditText to detect if the amount of characters is changed to two and did not change from three to two and remove "/" if a number before "/" is removed:

edit_text.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(p0: Editable?) {}

    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}

    override fun onTextChanged(p0: CharSequence?, start: Int, removed: Int, added: Int) {
        if (start == 1 && start+added == 2 && p0?.contains('/') == false) {
            edit_text.setText(p0.toString() + "/")
        } else if (start == 3 && start-removed == 2 && p0?.contains('/') == true) {
            edit_text.setText(p0.toString().replace("/", ""))
        }
    }
})
jobbert
  • 3,297
  • 27
  • 43
  • 1
    can you check, there is a type here: editText.setText(editText.getText() += "/"; its like a bracket is not closed. – j2emanue Jul 30 '17 at 07:08
  • By this approach, users have to enter two numbers at once replacing a singular number in order to get a slash to show up. I think you may need to read up on what "before" and "count" actually represent. [Documentation for onTextChanged](https://developer.android.com/reference/android/text/TextWatcher.html) so for example, as a user is entering characters one by one, before will always be "zero" unless backspacing a character, count will always be one unless the user selects a few characters and deletes them or pastes text. – Riot Goes Woof Sep 07 '17 at 15:56
  • @RiotGoesWoof Can you give me reproduction steps to show me what you mean? – jobbert Sep 08 '17 at 06:53
  • 1
    had to crop a bit for the character limit, but hopefully that gives you a good idea of what I meant. Another problem with this solution more falls in line with "what if the user just decides to type 118. or 12018. Most users will see the "MM/yy" specification, but for those that don't, they could find themselves very frustrated very quickly with an EditText that now won't cooperate with what they want to do. This is why I generally use pickers when dealing with dates. – Riot Goes Woof Sep 08 '17 at 21:50
  • I had an earlier comment, but i deleted it because i was way wrong. I'm not really sure where the numbers I was getting yesterday were coming from when I tested this... Either way, your solution above should work, but problems do arise like the one I mentioned. – Riot Goes Woof Sep 08 '17 at 22:09
  • Yeah I prefer datapickers and timepickers too, but that's not what's asked in the question. I get your problem with the missing input validation, but I didn't add it so that the answer keeps one purpose. – jobbert Sep 09 '17 at 09:15
  • 4
    this creates an infinite loop: changing text inside onTextChanged event triggers the same event over and over and over... again. – Wheel Builder Jul 12 '18 at 03:44
  • 1
    @SomeoneSomewhere Edited the answer so this don't happen but kept the rest of my answer that is ripped in other answers. – jobbert Apr 17 '19 at 11:23
  • You should make changes on "editable", not on edittext itself, cause that triggers TextWatcher events again and again. – Serdar Samancıoğlu Oct 22 '19 at 14:04
  • Please add this code at the and off first if block -> edit_text!!.setSelection(edit_text!!.text.toString().length) – ikbal Apr 21 '21 at 11:35
  • it doesn't work correctly, and when you remove `/` and add third digit then `/` won't be added – user924 Aug 09 '21 at 18:34
3
private static final String EXP_DATE_REGAX = "(0[1-9]|1[0-2])[0-9]{2}";

if (!edt_expiry_date.getText().toString().isEmpty()) {

                    Pattern pattern = Pattern.compile(EXP_DATE_REGAX);
                    Matcher matcher = pattern.matcher(edt_expiry_date.getText().toString());

                        if(!matcher.find()){
                            edt_expiry_date.setError("Invalid Expiry Date");
                        }else{
                            Calendar c = Calendar.getInstance();

                            int year = c.get(Calendar.YEAR) % 100;
                            int month = c.get(Calendar.MONTH)+1;

                            if(Integer.parseInt((edt_expiry_date.getText().toString().substring(2)))>=year){
                               if(Integer.parseInt((edt_expiry_date.getText().toString().substring(2)))==year){
                                   if(Integer.parseInt((edt_expiry_date.getText().toString().substring(0,2)))>=month){
                                       str_expiry_date = edt_expiry_date.getText().toString();
                                       dialog.dismiss();
                                   }else{
                                       edt_expiry_date.setError("Invalid Expiry Date");
                                   }
                               }else{
                                   str_expiry_date = edt_expiry_date.getText().toString();
                                   dialog.dismiss();
                               }


                            }else{
                                edt_expiry_date.setError("Invalid Expiry Date");
                            }

                           // dialog.dismiss();

                    }

                } else {
                    edt_expiry_date.setError("Enter Expiry Date");
                }
vm345
  • 813
  • 12
  • 28
Rohit269
  • 91
  • 4
  • 6
0

The issues and string manipulation needed with text based date entry just doesn't seem worth it here. What happens if a user goes back to the beginning of the string and starts typing? What happens if a user starts entering the wrong format? You have to account for a ton of cases without having a very good way of controlling what the user can input. In these instances, I'd just use a DatePicker like the one in this solution.

You can keep the EditText if you have your heart set on it, and just set the onClickListener to bring up the DatePicker when selected. I recommend setting these two fields on it though:

android:cursorVisible="false"
android:focusable="false"

This way the EditText won't look like it can be typed in. My only suggestion or addition to the other answer would be a "Clear" button so the EditText value can be wiped if needed. To do so, just add it via the neutral button like so:

builder.setView(dialog)
        // Add action buttons
        .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int id) {
                listener.onDateSet(null, yearPicker.getValue(), monthPicker.getValue(), 0);
            }
        })
        .setNeutralButton(R.string.clear, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                //check for a month value of -1 in onDateSet to clear current field
                listener.onDateSet(null, -1, -1, 0);
            }
        })
        .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                MonthYearPickerFragment.this.getDialog().cancel();
            }
        });

Then to check and see if the field should be cleared, just do something like this:

@Override
public void onDateSet(DatePicker datePicker, int i, int i1, int i2) {
    if (i > -1){
        et.setText(i1 + "/" + i);
    } else {
        et.setText("");
    }
}
Riot Goes Woof
  • 3,504
  • 5
  • 20
  • 35
0

In Kotlin for the format like

"MM/YY"

you can use this,

et_expire_date.addTextChangedListener(object : TextWatcher {

        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
            var working = s.toString()
            var isValid = true
            if (working.length == 2 && before == 0) {
                if (Integer.parseInt(working) < 1 || Integer.parseInt(working) > 12) {
                    isValid = false
                } else {
                    working += "/"
                    et_expire_date.setText(working)
                    et_expire_date.setSelection(working.length)
                }
            } else if (working.length == 5 && before == 0) {
                val enteredYear = working.substring(3)
                val currentYear = Calendar.getInstance().get(Calendar.YEAR) % 100//getting last 2 digits of current year i.e. 2018 % 100 = 18
                if (Integer.parseInt(enteredYear) < currentYear) {
                    isValid = false
                }
            } else if (working.length != 5) {
                isValid = false
            }

            if (!isValid) {
                et_expire_date.error = getString(R.string.enter_valid_date_mm_yy)
            } else {
                et_expire_date.error = null
            }

        }

        override fun afterTextChanged(s: Editable) {}

        override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
    })

where et_expire_date is edittext for date input

Kishan Solanki
  • 13,761
  • 4
  • 85
  • 82
-1

Use TextWatcher, example:

EditText editText = (EditText) findViewById(R.id.yourEditTextId);
yourEditText.addTextChangedListener(new TextWatcher() {

public void afterTextChanged(Editable s) {
  // Do what you want here
  String myStr = s.toString();
  // Check if MM and do what you want
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

public void onTextChanged(CharSequence s, int start, int before, int count) {}
});
Mercury
  • 7,430
  • 3
  • 42
  • 54
-1

Kotlin version of @Goodlife

override fun afterTextChanged(s: Editable?) {
                if (s!!.isNotEmpty() && (s.length % 3) == 0) {
                    val c = s[s.length-1]
                    if (c == '/') {
                        s.delete(s.length-1, s.length)
                    }
                }
                if (s.isNotEmpty() && (s.length % 3) == 0) {
                    val c = s[s.length-1]
                    if (Character.isDigit(c) && TextUtils.split(s.toString(), "/").size <= 2) {
                        s.insert(s.length-1, "/")
                    }
                }
            }
aleixrr
  • 461
  • 4
  • 18
-1

Simple Answer, set length 5 for mm/yy or 7 for mm/yyyy

    YourEditText.addTextChangedListener(new 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) {

                int len=s.toString().length();

                if (before == 0 && len == 2)
                    YourEditText.append("/");
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
Gundu Bandgar
  • 2,593
  • 1
  • 17
  • 21