を使用してユーザー入力を "0.05€" のようにマスクするのと同じように、 をEditText
" " 形式に従うようにフォーマットするにはどうすればよいですか。文字を制限したり、日付を検証したりすることについて話しているのではなく、以前の形式にマスキングしているだけです。dd/mm/yyyy
TextWatcher
11 に答える
私はこれTextWatcher
をプロジェクトのために書きました。うまくいけば、誰かの役に立ちます。ユーザーが入力した日付は検証されないことに注意してください。ユーザーが日付の入力を完了していない可能性があるため、フォーカスが変更されたときに処理する必要があります。
更新 25/06より良い最終コードに到達するかどうかを確認するために wiki にしました。
2006 年 7 月の更新 最終的に、ある種の検証をウォッチャー自体に追加しました。無効な日付で次のことを行います。
- 月が 12 より大きい場合は、12 (12 月) になります。
- 日付が選択した月の日付より大きい場合は、その月の最大値にします。
- 年が範囲内にない場合は、範囲内
1900-2100
になるように変更します
この検証は私のニーズに合っていますが、少し変更したい人もいるかもしれません。範囲は簡単に変更できToast
ます。たとえば、この検証をメッセージにフックして、日付が変更されたことをユーザーに通知できます。無効。
このコードでは、これがアタッチされているEditText
呼び出し元への参照があると仮定します。これは次のように行うことができます。date
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());
}
}
他の 2 つの関数も実装する必要があるためです。
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void afterTextChanged(Editable s) {}
};
これにより、次のような効果が生まれます。文字を削除または挿入すると、dd/mm/yyyy
マスクが表示または非表示になります。コードをできるだけシンプルにしようとしたので、他のフォーマット マスクに合わせて簡単に変更できるはずです。
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);
Juan Cortés の wiki は魅力的に機能します https://stackoverflow.com/a/16889503/3480740
ここで私のKotlinバージョン
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) {
}
})
}
独自のフォーマットを作成するのに 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) {
}
});
}