您好我正在编写一个edittext,其中我想要MM / YY格式的信用卡有效期。 我想实现的算法如下: 如果用户输入2到9之间的任何内容。我将文本输入更改为02 /到09 / 如果用户输入1,那么我等待下一个数字并检查int值月份是否小于12。 这是我的代码。
@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 {
}
}
这一功能正常,直到我按下软键返回按钮。反斜杠永远不会回头。 原因是第二个if条件总是满足。 我很困惑如何解决这个问题。如何处理后面的后退按钮?请帮忙。
答案 0 :(得分:11)
请参阅上面的评论以了解您的问题。您可以使用它来验证textwatcher的用户输入:
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;
答案 1 :(得分:4)
上面的@alex解决方案很好,但在几个实例中失败了。就像你试图删除斜杠一样,因为当你试图删除斜杠时,它永远不会真正达到(s.length(=)=&amp;&amp; mLastInput.endsWith(“/”))当它(如果( s.length()== 2&amp;&amp;!mLastInput.endsWith(“/”)因此它会给出斜杠不会删除的错觉。
如果用户完成日期,即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
}
}
当用户点击“提交”时,我会再次检查“月”,以便更加确定。
String expdate[] = mExpiryDate.getText().toString().split("/");
if(Integer.ParseInt(expDate[0]) > 12){
// Toast message "wrong date format"....
}
我希望这有帮助......
答案 2 :(得分:1)
愿你能做到这样:
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个之后防止这种情况。
希望它会帮助你...... !!!
答案 3 :(得分: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/"
/>
答案 4 :(得分:0)
我使用了Uche Dim的解决方案,并修复了一些问题并清理了代码。
所以我代码中的关键改进是:
所有这些几乎就像是Play商店的新卡到期字段。
我创建了一个Kotlin类,但还为Java添加了用法。
CardExpiryTextWatcher类:
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))
}
}
}
用法(科特琳):
CardExpiryTextWatcher.attachTo(inputCardExpiry, mServerDate, object : CardExpiryTextWatcher.DateListener {
override fun onExpiryEntered(date: Date) {
// TODO implement your handling
}
})
用法(Java):
CardExpiryTextWatcher.attachTo(inputCardExpiry, mServerDate, new CardExpiryTextWatcher.DateListener() {
@Override
public void onExpiryEntered(@NonNull Date date) {
// TODO implement your handling
}
});
注意::inputCardExpiry
是包含编辑文本的InputTextLayout
。
答案 5 :(得分:0)
将TextWatcher添加到您的EditText中,并使用REGEX进行验证。
TextWatcher:
GUI
使用REGEX进行验证:
etCardExpiry.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@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("/"));
}
}
}
});