在这段代码中,我将假设我们有一个名为 date 的 EditText 的引用,它附加了 TextWatcher ,这可以这样做:
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();
当用户更改 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());
}
}
我们还实现了其他两个功能,因为我们必须这样做
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) {}
};
import android.text.Editable;
import android.text.TextWatcher;
import java.util.Locale;
/**
* Adds slashes to a date so that it matches mm/dd/yyyy.
*
* Created by Mark Miller on 12/4/17.
*/
public class DateTextWatcher implements TextWatcher {
public static final int MAX_FORMAT_LENGTH = 8;
public static final int MIN_FORMAT_LENGTH = 3;
private String updatedText;
private boolean editing;
@Override
public void beforeTextChanged(CharSequence charSequence, int start, int before, int count) {
}
@Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
if (text.toString().equals(updatedText) || editing) return;
String digitsOnly = text.toString().replaceAll("\\D", "");
int digitLen = digitsOnly.length();
if (digitLen < MIN_FORMAT_LENGTH || digitLen > MAX_FORMAT_LENGTH) {
updatedText = digitsOnly;
return;
}
if (digitLen <= 4) {
String month = digitsOnly.substring(0, 2);
String day = digitsOnly.substring(2);
updatedText = String.format(Locale.US, "%s/%s", month, day);
}
else {
String month = digitsOnly.substring(0, 2);
String day = digitsOnly.substring(2, 4);
String year = digitsOnly.substring(4);
updatedText = String.format(Locale.US, "%s/%s/%s", month, day, year);
}
}
@Override
public void afterTextChanged(Editable editable) {
if (editing) return;
editing = true;
editable.clear();
editable.insert(0, updatedText);
editing = false;
}
}
4
使用JuanCortés代码的更简洁的方法是将它放在一个类中:
public class DateInputMask implements TextWatcher {
private String current = "";
private String ddmmyyyy = "DDMMYYYY";
private Calendar cal = Calendar.getInstance();
private EditText input;
public DateInputMask(EditText input) {
this.input = input;
this.input.addTextChangedListener(this);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.toString().equals(current)) {
return;
}
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;
input.setText(current);
input.setSelection(sel < current.length() ? sel : current.length());
}
@Override
public void afterTextChanged(Editable s) {
}
}
5 回答
我为一个项目写了这个
TextWatcher
,希望它对某人有帮助 . 请注意,它确认 not 验证用户输入的日期,并且您应该在焦点更改时处理该日期,因为用户可能尚未完成输入日期 .Update 25/06 制作一个wiki,看看我们是否达到了更好的最终代码 .
Update 07/06 我终于为观察者本身添加了某种验证 . 它将执行以下无效日期:
如果月份大于12,则为12(12月)
如果日期大于所选月份的日期,请将其作为该月份的最大值 .
如果年份不在
1900-2100
范围内,请将其更改为在范围内这个验证符合我的需要,但有些人可能想稍微更改一下,范围很容易更改,你可以将这个验证挂钩到例如
Toast
消息,以通知用户我们已经修改了他/她的日期,因为它无效 .在这段代码中,我将假设我们有一个名为
date
的EditText
的引用,它附加了TextWatcher
,这可以这样做:当用户更改
EditText
的文本时我们还实现了其他两个功能,因为我们必须这样做
这会产生以下效果,删除或插入字符将显示或隐藏
dd/mm/yyyy
掩码 . 因为我试图让代码尽可能简单,所以应该很容易修改以适应其他格式的掩码 .此答案不会对剩余的无类型数字应用完整掩码 . 但是,它是相关的,是我需要的解决方案 . 它的工作方式与
PhoneNumberFormattingTextWatcher
的工作方式类似 .在您键入时,它会添加斜杠以分隔格式为
mm/dd/yyyy
的日期 . 它没有做任何验证 - 只是格式化 .不需要
EditText
参考 . 只需设置监听器即可 .myEditText.addTextChangedListener(new DateTextWatcher());
使用JuanCortés代码的更简洁的方法是将它放在一个类中:
然后你可以重复使用它
目前的答案非常好,并帮助指导我自己的解决方案 . 我决定发布自己的解决方案有几个原因,即使这个问题已经有一个有效的答案:
我在Kotlin工作,而不是Java . 发现自己遇到同样问题的人将不得不翻译当前的解决方案 .
我想写一个更清晰的答案,以便人们可以更容易地适应他们自己的问题 .
正如dengue8830所建议的,我在一个类中封装了这个问题的解决方案,所以任何人都可以使用,甚至不用担心实现 .
要使用它,只需执行以下操作:
解决方案如下所示:
尝试使用可以解决此问题的库,因为屏蔽它不是开箱即用的 . 有很多极端情况(比如在已经屏蔽的文本中间添加/删除字符)并正确处理这个问题,你最终会得到很多代码(和bug) .
以下是一些可用的库:
https://github.com/egslava/edittext-mask
https://github.com/dimitar-zabaznoski/MaskedEditText
https://github.com/pinball83/Masked-Edittext
https://github.com/RedMadRobot/input-mask-android
https://github.com/santalu/mask-edittext
**请注意,在编写这些库时并非没有问题,因此您有责任选择最适合您的库并测试代码 .