我应该如何设置
EditText
的格式,使其遵循"dd/mm/yyyy
"格式,就像使用TextWatcher
对用户输入进行掩码处理以呈现"0.05€"一样。我不是在谈论限制字符或验证日期,只是要将其掩码为之前的格式。EditText
的格式,使其遵循"dd/mm/yyyy
"格式,就像使用TextWatcher
对用户输入进行掩码处理以呈现"0.05€"一样。我不是在谈论限制字符或验证日期,只是要将其掩码为之前的格式。我为一个项目编写了这个 TextWatcher
,希望它能对某些人有所帮助。请注意,它不会验证用户输入的日期,你应该在焦点改变时处理它,因为用户可能没有完成输入日期。
更新 06/25:将其变成wiki以查看是否可以达到更好的最终代码。
更新 06/07: 我最终向监视器本身添加了某种形式的验证。对于无效日期,它将执行以下操作:
1900-2100
范围内,则将其更改为该范围内的年份。这个验证符合我的需求,但是你们中的一些人可能想要稍微改变一下,范围很容易改变,而且你可以将这些验证钩子连接到Toast
消息上,以通知用户我们已修改他/她的日期,因为它是无效的。
在此代码中,我将假设我们有一个称为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) {}
};
这会产生以下效果,删除或插入字符将显示或隐藏dd/mm/yyyy
掩码。由于我试图保持代码尽可能简单,因此修改以适应其他格式掩码应该很容易。
现有答案非常好,帮助我指导了我的解决方案。尽管这个问题已经有一个有效的答案,但我还是决定发布自己的解决方案,原因如下:
要使用它,只需像这样做:
解决方案如下:
class DateInputMask(val input : EditText) {
fun listen() {
input.addTextChangedListener(mDateEntryWatcher)
}
private val mDateEntryWatcher = object : TextWatcher {
var edited = false
val dividerCharacter = "/"
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
if (edited) {
edited = false
return
}
var working = getEditText()
working = manageDateDivider(working, 2, start, before)
working = manageDateDivider(working, 5, start, before)
edited = true
input.setText(working)
input.setSelection(input.text.length)
}
private fun manageDateDivider(working: String, position : Int, start: Int, before: Int) : String{
if (working.length == position) {
return if (before <= position && start < position)
working + dividerCharacter
else
working.dropLast(1)
}
return working
}
private fun getEditText() : String {
return if (input.text.length >= 10)
input.text.toString().substring(0,10)
else
input.text.toString()
}
override fun afterTextChanged(s: Editable) {}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
}
}
使用Juan Corté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) {
}
}
那么您可以重新使用它
new DateInputMask(myEditTextInstance);
请注意,编写本文时这些库并不完美,因此选择最适合您的库并测试代码是您的责任。
fun setBirthdayEditText() {
birthdayEditText.addTextChangedListener(object : TextWatcher {
private var current = ""
private val ddmmyyyy = "DDMMYYYY"
private val cal = Calendar.getInstance()
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
if (p0.toString() != current) {
var clean = p0.toString().replace("[^\\d.]|\\.".toRegex(), "")
val cleanC = current.replace("[^\\d.]|\\.", "")
val cl = clean.length
var sel = cl
var i = 2
while (i <= cl && i < 6) {
sel++
i += 2
}
//Fix for pressing delete next to a forward slash
if (clean == 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
var day = Integer.parseInt(clean.substring(0, 2))
var mon = Integer.parseInt(clean.substring(2, 4))
var year = Integer.parseInt(clean.substring(4, 8))
mon = if (mon < 1) 1 else if (mon > 12) 12 else mon
cal.set(Calendar.MONTH, mon - 1)
year = if (year < 1900) 1900 else if (year > 2100) 2100 else 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 = if (day > cal.getActualMaximum(Calendar.DATE)) cal.getActualMaximum(Calendar.DATE) else 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 = if (sel < 0) 0 else sel
current = clean
birthdayEditText.setText(current)
birthdayEditText.setSelection(if (sel < current.count()) sel else current.count())
}
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun afterTextChanged(p0: Editable) {
}
})
}
val cleanC = current.replace("[^\\d.]|\\.", "")
应该是 val cleanC = current.replace("[^\\d.]|\\.".toRegex(), "")
。 - Estevex没有验证的Kotlin版本
editText.addTextChangedListener(object : TextWatcher{
var sb : StringBuilder = StringBuilder("")
var _ignore = false
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
if(_ignore){
_ignore = false
return
}
sb.clear()
sb.append(if(s!!.length > 10){ s.subSequence(0,10) }else{ s })
if(sb.lastIndex == 2){
if(sb[2] != '/'){
sb.insert(2,"/")
}
} else if(sb.lastIndex == 5){
if(sb[5] != '/'){
sb.insert(5,"/")
}
}
_ignore = true
editText.setText(sb.toString())
editText.setSelection(sb.length)
}
})
PhoneNumberFormattingTextWatcher
。mm/dd/yyyy
。它不进行任何验证 - 只进行格式化。
不需要EditText
参考。只需设置监听器即可。
myEditText.addTextChangedListener(new DateTextWatcher());
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;
}
}
class DateMask : TextWatcher {
private var updatedText: String? = null
private var editing: Boolean = false
companion object {
private const val MAX_LENGTH = 8
private const val MIN_LENGTH = 2
}
override fun beforeTextChanged(charSequence: CharSequence, start: Int, before: Int, count: Int) {
}
override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {
if (text.toString() == updatedText || editing) return
var digits = text.toString().replace("\\D".toRegex(), "")
val length = digits.length
if (length <= MIN_LENGTH) {
digits = validateMonth(digits)
updatedText = digits
return
}
if (length > MAX_LENGTH) {
digits = digits.substring(0, MAX_LENGTH)
}
updatedText = if (length <= 4) {
digits = validateDay(digits.substring(0, 2), digits.substring(2))
val month = digits.substring(0, 2)
val day = digits.substring(2)
String.format(Locale.US, "%s/%s", month, day)
} else {
digits = digits.substring(0, 2) + digits.substring(2, 4) + validateYear(digits.substring(4))
val month = digits.substring(0, 2)
val day = digits.substring(2, 4)
val year = digits.substring(4)
String.format(Locale.US, "%s/%s/%s", month, day, year)
}
}
private fun validateDay(month: String, day: String): String {
val arr31 = intArrayOf(1, 3, 5, 7, 8, 10, 12)
val arr30 = intArrayOf(4, 6, 9, 11)
val arrFeb = intArrayOf(2)
if (day.length == 1 &&
((day.toInt() > 3 && month.toInt() !in arrFeb)
|| (day.toInt() > 2 && month.toInt() in arrFeb))) {
return month
}
return when (month.toInt()) {
in arr31 -> validateDay(month, arr31, day, 31)
in arr30 -> validateDay(month, arr30, day, 30)
in arrFeb -> validateDay(month, arrFeb, day, 29)
else -> "$month$day"
}
}
private fun validateDay(month: String, arr: IntArray, day: String, maxDay: Int): String {
if (month.toInt() in arr) {
if (day.toInt() > maxDay) {
return "$month${day.substring(0, 1)}"
}
}
return "$month$day"
}
private fun validateYear(year: String): String {
if (year.length == 1 && (year.toInt() in 3..9 || year.toInt() == 0)) {
return ""
}
if (year.length == 2 && year.toInt() !in 19..20) {
return year.substring(0, 1)
}
return year
}
private fun validateMonth(month: String): String {
if (month.length == 1 && month.toInt() in 2..9) {
return "0$month"
}
if (month.length == 2 && month.toInt() > 12) {
return month.substring(0, 1)
}
return month
}
override fun afterTextChanged(editable: Editable) {
if (editing) return
editing = true
editable.clear()
editable.insert(0, updatedText)
editing = false
}
mEditText?.addTextChangedListener(dateMask)
android:inputType="date"
添加到您的EditText
中。花了6个小时制作了自己的格式。 只需试用一下,如果您喜欢它,请查看代码。您可以在任何地方编辑日期,例如仅限于天数、月份。 它将自动转义'/'字符并更新下一个数字。
// initialized with the current date
String date = new SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()).format(new Date());
edit_date_editEntity.setText(date);
public void formatDate(){
edit_date_editEntity.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
String ss = s.toString();
if (after==0) { // when a single character is deleted
if (s.charAt(start) == '/') { // if the character is '/' , restore it and put the cursor at correct position
edit_date_editEntity.setText(s);
edit_date_editEntity.setSelection(start);
}
else if (s.charAt(start) == '-') { // if the character is '-' , restore it and put the cursor at correct position
edit_date_editEntity.setText(s);
edit_date_editEntity.setSelection(start);
}
else if (ss.charAt(start) >= '0' && ss.charAt(start) <= '9') { // if the character is a digit, replace it with '-'
ss = ss.substring(0, start) + "-" + ss.substring(start +1, ss.length());
edit_date_editEntity.setText(ss);
edit_date_editEntity.setSelection(start);
}
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String ss = s.toString();
if (before==0 ){ // when a single character is added
if (edit_date_editEntity.getSelectionStart()==3 || edit_date_editEntity.getSelectionStart()==6) {
// if the new character was just before '/' character
// getSelection value gets incremented by 1, because text has been changed and hence cursor position updated
// Log.d("test", ss);
ss = ss.substring(0, start) + "/" + ss.substring(start, start + 1) + ss.substring(start + 3, ss.length());
// Log.d("test", ss);
edit_date_editEntity.setText(ss);
edit_date_editEntity.setSelection(start + 2);
}
else {
if (edit_date_editEntity.getSelectionStart()==11){
// if cursor was at last, do not add anything
ss = ss.substring(0,ss.length()-1);
edit_date_editEntity.setText(ss);
edit_date_editEntity.setSelection(10);
}
else {
// else replace the next digit with the entered digit
ss = ss.substring(0, start + 1) + ss.substring(start + 2, ss.length());
edit_date_editEntity.setText(ss);
edit_date_editEntity.setSelection(start + 1);
}
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
cal
声明为private Calendar cal = Calendar.getInstance();
之前,我一直很困难,否则我会在cal.set(Calendar.MONTH, mon-1);
部分遇到NullPointerException
。@joao2fast4u @LeonardoOtto - Thomas