【Android】 TextWatcherを使って、金額入力時にカンマを加える。

## 今回の目的

EditTextで金額を入力する時、月並みだが、動的に入力した数値をカンマ区切りに変換できないか、
色々調べたところ、ある程度制御が出来るようになったので、紹介していこうと思います。

## 使用する環境
- AndroidStudio Ver 3.4.1
- Android 10.0 (Q) API 29

## TextWatcher

[TextWatcher (AndroidDeveloper)](https://developer.android.com/reference/android/text/TextWatcher)

⇨ EditTextの入力を監視するEventListener（編集前の値を取得したり、編集後の値やポジションを取得する等、出来る）

色々詳しいことは、参考サイトや↑のリファレンスを参照してください。

## 実装に当たって

まずはImplements(実装)していきます。

```MainActivity.java
public class SampleClass1 extends AppCompatActivity implements TextWatcher
```


その後、エラーが出て、「専用のクラスをOverRideせよ」と言われるのでどこかに以下のクラス群を設置します。

```MainActivity.java
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    }

    @Override
    public void afterTextChanged(Editable s) {
    }

```

とここまでは、調べると色々出てくるので、詳しい内容は参考サイトを見てみてください。


## 実装

では実際に実装していきます

```MainActivity.java
    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        //入力直後のテキストを取得
        if (!chg_flg){
            prevText = charSequence.toString();
        }
    }

    @Override
    public void onTextChanged(CharSequence charSequence, int cur, int del, int add) {
        try {
            if (chg_flg) { return; }  //変更途中はスルー

            StringBuilder str_claft = new StringBuilder();
            String str = charSequence.toString();

            //カンマを消そうとしているかチェックする
            if (!this.CheckInputCash(prevText, str)) {
                //カンマ手前の数字を削除する
                cur -= 1;
                str_claft.append(str);
                str_claft.deleteCharAt(cur);
                str = str_claft.toString();
            }
 
            //ここでflgを立てておく
            //この対策をしておかないと、後々２重で呼び出されることになってめんどくさい。
            chg_flg = true;

            //ここでカンマを一旦外す。
            str = str.replace(",", "");

            int kngk = 0;
            String tmp_kngk = "";

            //現在のカーソル位置を編集用変数に格納。
            int sel_pos = cur;

            //文字数が３文字以上の場合、カンマ区切りに整形する。
            if (str.length() > 3) {
                kngk = Integer.parseInt(str);
                tmp_kngk = String.format("%,d", kngk);
            } else {
                //0からスタートの場合は、「０」を削除する。
                if (prevText.equals("0")) {
                    str = str.replace("0", "");
                }
                tmp_kngk = str;
            }

            if (cur == 0 && del != 0 && tmp_kngk.length() == 0) {
                //削除時、０桁になる場合は「０」をセットする。
                tmp_kngk = "0";
                sel_pos = 1;
            } else {
                //追加時、追加後の文字数によってPostionを移動する。
                if (del == 0){
                    if (tmp_kngk.length() == 9 || tmp_kngk.length() == 5) {
                        // 特定の文字数の時は＋２　　　　　　ex)1,234,567 or 1,234
                        sel_pos += 2;
                    } else {
                        // それ以外は＋１
                        sel_pos += 1;
                    }
                }
            }

            //範囲以上になる場合は、末尾にPostionをセット。
            if (sel_pos > tmp_kngk.length()) {
                sel_pos = tmp_kngk.length();
            }

            //新規と編集でEditTextの参照先が変わる場合は、MODE管理をしておく。
            //変わらないであれば、if文自体が要らない。
            if (form_mode.equals("00")){
                INPUT_KNGK.setText(tmp_kngk);
                INPUT_KNGK.setSelection(sel_pos);
            }else if(form_mode.equals("01")){
                EDIT_KNGK.setText(tmp_kngk);
                EDIT_KNGK.setSelection(sel_pos);
            }

            //終わったら、flgを戻す
            chg_flg = false;

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void afterTextChanged(Editable editable) {
        try{
            if (!chg_flg){return;}  //変更途中はスルー
            String str = editable.toString();

            //ここでチェック機能を走らせることも出来る。
            if (str.length() > 11){
                MSGFORM1.setVisibility(View.VISIBLE);
                ERRMSG1.setVisibility(View.VISIBLE);
                ERRMSG1.setText("入力できる桁数を超えています。9桁以内で入力してください。");
            }else{
                MSGFORM1.setVisibility(View.GONE);
                ERRMSG1.setVisibility(View.GONE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

```

```MainActivity.java
    /** 削除対象がカンマかどうかチェックする **/
    public boolean CheckInputCash(String before,String after){

        boolean result = true;

        try{
            String str_before = before.replace(",","");
            String str_after = after.replace(",","");

            if (str_before.length() == str_after.length()){
                //カンマ外して文字数が一緒ならば、カンマを削除しようとしている
                result = false;
            }else{
                result = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return result;
    }
```

## ポイント


- カンマ区切りに整形する場合、カーソルの持って行き場を指定しないといけないので、そこが大変。
  - カーソルがカンマに当たっている状態で削除キーを押すと、後の方にカーソルが当たってしまうので、制御してあげないといけない。
  - というより、何が一番大変ってカーソルの位置指定のめんどくささ。
- チェック機能をつけるであれば、「afterTextChanged()」で行う。
  - それ以外だと、変な感じの挙動になる。
- EditTextはPrivateで扱えるように定義してあげないといけない。 
  - 整形後の内容を入れるため。


## 終わりに

どうでしょうか？
カーソルの扱い方は慣れないと難しいですが、デバッグして引数の値を見ていくと、色々掴めてきます。

他のサイトで見るよりかは簡単に実装できたように感じますが、他に何か参考になりそうなサイトがあれば、ぜひコメントください。

それでは、また。

## 参考サイト

- [入力を監視するTextWatcher/nyanのアプリ開発](https://akira-watson.com/android/textwatcher.html)
  - 個人的にはここのサイトがオススメです。（わかりやすい）
- [TextWatcherでリアルタイムに値を取得する方法(Android)](https://qiita.com/JP_Hiroto/items/68f80df760162e905d99)