web-dev-qa-db-ja.com

dd / mm / yyyy日付形式を表示するためにEditTextをマスクする方法

EditTextをフォーマットして「dd/mm/yyyy "は、TextWatcherを使用して、マスクを使用して「0.05€」のようにユーザー入力をフォーマットするのと同じ方法でフォーマットします。文字の制限については話していません。以前の形式にマスクするだけで、日付を検証します。

47
Juan Cortés

プロジェクト用にこれを書いたTextWatcher、うまくいけば誰かに役立つでしょう。 notはユーザーが入力した日付を検証することに注意してください。ユーザーが日付の入力を完了していない可能性があるため、フォーカスが変更されたときにそれを処理する必要があります。

pdate 25/06最終的なコードが改善されるかどうかを確認するためにwikiにしました。

更新07/06最後に、ウォッチャー自体に何らかの検証を追加しました。無効な日付を使用して以下を実行します。

  • 月が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マスクが表示または非表示になります。コードをできるだけシンプルにしようとしたため、他のフォーマットマスクに合わせて簡単に変更できるはずです。

enter image description here

103
Juan Cortés

現在の答えは非常によく、私自身の解決策に私を導くのに役立ちました。この質問にはすでに有効な答えがありますが、自分のソリューションを投稿することにした理由はいくつかあります。

  • 私はJavaではなくKotlinで働いています。同じ問題を抱えている人は、現在のソリューションを翻訳する必要があります。
  • 人々が自分の問題により簡単に適応できるように、より読みやすい回答を書きたかったのです。
  • Dengue8830で示唆されているように、この問題の解決策をクラスにカプセル化したため、誰でも実装を心配することなく使用できます。

それを使用するには、次のようにします。

  • DateInputMask(mEditText).listen()

そして、解決策を以下に示します。

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) {}
    }
}
8
Ícaro Mota

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) {

}
}

その後、あなたはそれを再利用することができます

new DateInputMask(myEditTextInstance);
6
David Rearte

追加 Android:inputType="date"EditText

1
Jim

検証なしの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)

        }
    })
1

JuanCortésのwikiは魅力のように機能します https://stackoverflow.com/a/16889503/348074

ここに私の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) {

        }
    })
}
1
ivoroto

マスキングはすぐに使用できないため、この問題を解決するライブラリを使用してみてください。多くのコーナーケース(既にマスクされているテキストの途中で文字を追加/削除するなど)があり、これを適切に処理するには、多くのコード(およびバグ)が発生します。

利用可能なライブラリは次のとおりです。
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

**これらのライブラリを書いている時点では問題がないわけではないので、自分に最も適したライブラリを選択してコードをテストするのはあなたの責任です。

1
kalimero

この回答では、残りの型指定されていない数字に完全なマスクは適用されません。しかし、それは関連しており、私が必要とした解決策です。 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;
    }
}
_
0
Markymark