Format end date in mm / yy format

Hi, I am writing edittext in which I need the expiration date of a credit card in MM / YY format. The algorithm that I want to implement is as follows: If the user enters something from 2 to 9. I change the text input to 02 / to 09 / If the user enters 1, then I wait for the next digit and check if the month is int less than 12. Here is my code for this.

@Override
            public void afterTextChanged(Editable s) { 
            String input = s.toString();
                if (s.length() == 1) {
                        int month = Integer.parseInt(input);
                        if (month > 1) {
                            mExpiryDate.setText("0" + mExpiryDate.getText().toString() + "/");
                            mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
                            mSeperator = true;
                        }

                }
                else if (s.length() == 2) {
                        int month = Integer.parseInt(input);
                        if (month <= 12) {
                            mExpiryDate.setText(mExpiryDate.getText().toString() + "/");
                            mExpiryDate.setSelection(mExpiryDate.getText().toString().length());                            
                            mSeperator = true;
                        }
                }
                else {

                }

            }

This works fine until I press a softkey button. Backslash never returns. The reason is the second, if the condition is always fulfilled. I am confused about how to solve this. How do I handle the back button inside aftertextchanged? Please, help.

+5
5

. , . , :

SimpleDateFormat formatter = 
    new SimpleDateFormat("MM/yy", Locale.GERMANY);
Calendar expiryDateDate = Calendar.getInstance();
try {
    expiryDateDate.setTime(formatter.parse(mExpiryDate.getText().toString()));
} catch (ParseException e) {
    //not valid
}
// expiryDateDate has a valid date from the user

, :

String lastInput ="";

@Override
public void afterTextChanged(Editable s) { 
     String input = s.toString();
     SimpleDateFormat formatter = new SimpleDateFormat("MM/yy", Locale.GERMANY);
    Calendar expiryDateDate = Calendar.getInstance();
    try {
        expiryDateDate.setTime(formatter.parse(input));
    } catch (ParseException e) {
        if (s.length() == 2 && !lastInput.endsWith("/")) {
            int month = Integer.parseInt(input);
            if (month <= 12) {
               mExpiryDate.setText(mExpiryDate.getText().toString() + "/");
            }
        }else if (s.length() == 2 && lastInput.endsWith("/")) {
            int month = Integer.parseInt(input);
            if (month <= 12) {
               mExpiryDate.setText(mExpiryDate.getText().toString().subStr(0,1);
            }
        }
        lastInput = mExpiryDate.getText().toString();
        //because not valid so code exits here
        return;
    }
    // expiryDateDate has a valid date from the user
    // Do something with expiryDateDate here
}

, :

String input = s.toString();
SimpleDateFormat formatter = new SimpleDateFormat("MM/yy", Locale.GERMANY);
Calendar expiryDateDate = Calendar.getInstance();
try {
   expiryDateDate.setTime(formatter.parse(input));
} catch (ParseException e) {

} catch (java.text.ParseException e) {
if (s.length() == 2 && !mLastInput.endsWith("/")) {
   int month = Integer.parseInt(input);
   if (month <= 12) {
      mExpiryDate.setText(mExpiryDate.getText().toString() + "/");
      mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
   }
}else if (s.length() == 2 && mLastInput.endsWith("/")) {
   int month = Integer.parseInt(input);
    if (month <= 12) {
       mExpiryDate.setText(mExpiryDate.getText().toString().substring(0,1));
       mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
    } else {
       mExpiryDate.setText("");
       mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
       Toast.makeText(getApplicationContext(), "Enter a valid month", Toast.LENGTH_LONG).show();
    }
} else if (s.length() == 1){
    int month = Integer.parseInt(input);
    if (month > 1) {
       mExpiryDate.setText("0" + mExpiryDate.getText().toString() + "/");
       mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
    }
}
else {

}
mLastInput = mExpiryDate.getText().toString();
return;
+11

@alex , . , , if (s.length() == 2 && mLastInput.endsWith( "/" )), , if ( s.length() == 2 &! mLastInput.endsWith( "/" ), , .

, i.e 08/16, , , , , - 0/1. @alex .

//Make sure for mExpiryDate to be accepting Numbers only
boolean isSlash = false; //class level initialization 
private void formatCardExpiringDate(Editable s){
    String input = s.toString();
    String mLastInput = "";

    SimpleDateFormat formatter = new SimpleDateFormat("MM/yy",     Locale.ENGLISH);
    Calendar expiryDateDate = Calendar.getInstance();

    try {
        expiryDateDate.setTime(formatter.parse(input));
    } catch (java.text.ParseException e) {
        if (s.length() == 2 && !mLastInput.endsWith("/") && isSlash) {
            isSlash = false;
            int month = Integer.parseInt(input);
            if (month <= 12) {
                     mExpiryDate.setText(mExpiryDate.getText().toString().substring(0, 1));
                mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
            } else {
                s.clear();
                mExpiryDate.setText("");
                mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
                Toast.makeText(context.getApplicationContext(), "Enter a valid month", Toast.LENGTH_LONG).show();
            }
        }else if (s.length() == 2 && !mLastInput.endsWith("/") && !isSlash) {
            isSlash = true;
            int month = Integer.parseInt(input);
            if (month <= 12) {
                mExpiryDate.setText(mExpiryDate.getText().toString() + "/");
                mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
            }else if(month > 12){
                edCardDate.setText("");
                mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
                s.clear();
                _toastMessage("invalid month", context);
            }


        } else if (s.length() == 1) {

            int month = Integer.parseInt(input);
            if (month > 1 && month < 12) {
                isSlash = true;
                mExpiryDate.setText("0" + mExpiryDate.getText().toString() + "/");
                mExpiryDate.setSelection(mExpiryDate.getText().toString().length());
            }
        }

        mLastInput = mExpiryDate.getText().toString();
        return;
    }
}
//wrap method formatCardExpiringDate around try catch or wrap the entire code in try catch, catching NumberFormateException. To take care of situations when s.length() == 2 and there is a a number in from of the slash
@Override
public void afterTextChanged(Editable s) { 
   try{
     formatCardExpiringDate(s)
    }catch(NumberFormatException e){
      s.clear(); 
      //Toast message here.. Wrong date formate

    }
}

, submit, .

String expdate[] = mExpiryDate.getText().toString().split("/");
if(Integer.ParseInt(expDate[0]) > 12){
  // Toast message "wrong date format".... 
}

, ...

+5

, :

boolean validateCardExpiryDate(String expiryDate) {
    return expiryDate.matches("(?:0[1-9]|1[0-2])/[0-9]{2}");
}

:

( ?): 0, 1-9 1, 0-2 "/" 0-9, . ... (01 - 12). ? 0, .

, ..!!!

+1

TextWatchers (, ViewModel) .

TextWatchers EditText.

InputFilter TextWatcher. :

:

/**
 * InputFilter to ensure user enters valid expiry date in a credit card.
 * User is only allowed to type from beginning-to-end without copy-pasting or inserting characters in the middle.
 * The user may enter any month 01 -> 12.
 * The user can enter, at minimum, the current year or any year that follows.
 *
 * Note: `inputType` of the EditText should be `number` and `digits` should be `0123456789/`.
 *
 * Created by W.K.S on 30/07/2017 (Licensed under GNU Public License - original author must be credited)
 */

public class CreditCardExpiryInputFilter implements InputFilter {

    private final String currentYearLastTwoDigits;

    public CreditCardExpiryInputFilter() {
        currentYearLastTwoDigits = new SimpleDateFormat("yy", Locale.US).format(new Date());
    }

    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        //do not insert if length is already 5
        if (dest != null & dest.toString().length() == 5) return "";
        //do not insert more than 1 character at a time
        if (source.length() > 1) return "";
        //only allow character to be inserted at the end of the current text
        if (dest.length() > 0 && dstart != dest.length()) return "";

        //if backspace, skip
        if (source.length() == 0) {
            return source;
        }

        //At this point, `source` is a single character being inserted at `dstart`. 
        //`dstart` is at the end of the current text.

        final char inputChar = source.charAt(0);

        if (dstart == 0) {
            //first month digit
            if (inputChar > '1') return "";
        }
        if (dstart == 1) {
            //second month digit
            final char firstMonthChar = dest.charAt(0);
            if (firstMonthChar == '0' && inputChar == '0') return "";
            if (firstMonthChar == '1' && inputChar > '2') return "";

        }
        if (dstart == 2) {
            final char currYearFirstChar = currentYearLastTwoDigits.charAt(0);
            if (inputChar < currYearFirstChar) return "";
            return "/".concat(source.toString());
        }
        if (dstart == 4){
            final String inputYear = ""+dest.charAt(dest.length()-1)+source.toString();
            if (inputYear.compareTo(currentYearLastTwoDigits) < 0) return "";
        }

        return source;
    }
}

CreditCardExpiryInputFilter EditText:

EditText expiryEditText = findViewById(this, R.id.edittext_expiry_date);
expiryEditText.setFilters(new InputFilter[]{new CreditCardExpiryInputFilter()});

xml inputType number digits 0123456789/:

<EditText
    android:id="@+id/edittext_expiry_date"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:inputType="number"
    android:digits="0123456789/"
    />
+1

Uche Dim, .

, :

  1. "13", "1".
  2. when the user starts to enter the year, after deleting the slash, a slash will be added to save the MM / yy format.

In general, it is almost like the expiration field of new cards in the Play Store.

I created a Kotlin class, but use for Java is also added.

CardExpiryTextWatcher Class:

class CardExpiryTextWatcher(private val mTextInputLayout: TextInputLayout,
                            private val mServerDate: Date,
                            private val mListener: DateListener) : TextWatcher {

    private val mExpiryDateFormat = SimpleDateFormat("MM/yy", Locale.US)
    private var mLastInput = ""

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

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    }

    @SuppressLint("SetTextI18n")
    override fun afterTextChanged(s: Editable) {
        val input = s.toString()
        when (s.length) {
            1 -> handleMonthInputForFirstCharacter(input)
            2 -> handleMonthInputForSecondCharacter(input)
            3 -> addSlashIfNotAddedAtEnd(input)
            5 -> validateDateAndCallListener(input)
        }
        mLastInput = mTextInputLayout.editText!!.text.toString()
    }

    private fun validateDateAndCallListener(input: String) {
        try {
            if (input[2] == '/') {
                val date = mExpiryDateFormat.parse(input)
                validateCardIsNotExpired(date)
            }
        } catch (e: ParseException) {
            mTextInputLayout.error = mTextInputLayout.context.getString(R.string.card_exp_date_error)
        }
    }

    private fun validateCardIsNotExpired(cardExpiry: Date) {
        if (DateUtils.isDateBefore(cardExpiry, mServerDate)) {
            mTextInputLayout.error = mTextInputLayout.context.getString(R.string.card_expired)
            return
        }
        mListener.onExpiryEntered(cardExpiry)
    }

    @SuppressLint("SetTextI18n")
    private fun addSlashIfNotAddedAtEnd(input: String) {
        val lastCharacter = input[input.length - 1]
        if (lastCharacter != '/') {
            val month = input.substring(0, 2)
            mTextInputLayout.editText!!.setText("$month/$lastCharacter")
            mTextInputLayout.editText!!.setSelection(mTextInputLayout.editText!!.text.toString().length)
        }
    }

    @SuppressLint("SetTextI18n")
    private fun handleMonthInputForSecondCharacter(input: String) {
        if (mLastInput.endsWith("/")) {
            return
        }
        val month = Integer.parseInt(input)
        if (month > 12) {
            mTextInputLayout.editText!!.setText(mLastInput)
            mTextInputLayout.editText!!.setSelection(mTextInputLayout.editText!!.text.toString().length)
            mTextInputLayout.error = mTextInputLayout.context.getString(R.string.card_exp_date_error)
        } else {
            mTextInputLayout.editText!!.setText("${mTextInputLayout.editText!!.text}/")
            mTextInputLayout.editText!!.setSelection(mTextInputLayout.editText!!.text.toString().length)
        }
    }

    @SuppressLint("SetTextI18n")
    private fun handleMonthInputForFirstCharacter(input: String) {
        val month = Integer.parseInt(input)
        if (month in 2..11) {
            mTextInputLayout.editText!!.setText("0${mTextInputLayout.editText!!.text}/")
            mTextInputLayout.editText!!.setSelection(mTextInputLayout.editText!!.text.toString().length)
        }
    }

    interface DateListener {
        fun onExpiryEntered(date: Date)
    }

    companion object {

        @JvmStatic
        fun attachTo(textInputLayout: TextInputLayout, serverDate: Date, listener: DateListener) {
            textInputLayout.editText!!.addTextChangedListener(
                    CardExpiryTextWatcher(textInputLayout, serverDate, listener))
        }
    }
}

Usage (Kotlin):

CardExpiryTextWatcher.attachTo(inputCardExpiry, mServerDate, object : CardExpiryTextWatcher.DateListener {
    override fun onExpiryEntered(date: Date) {
        // TODO implement your handling
    }
})

Usage (Java):

CardExpiryTextWatcher.attachTo(inputCardExpiry, mServerDate, new CardExpiryTextWatcher.DateListener() {
    @Override
    public void onExpiryEntered(@NonNull Date date) {
        // TODO implement your handling
    }
});

Note: inputCardExpiry is the InputTextLayoutone that contains the EditText.

+1
source

All Articles