Android: TextWatcherによる編集可能なEditText

AndroidでTextWatcherによる編集可能なEditTextの実装方法を整理する。
導入
Androidの開発で,テキストの入力欄のUIウィジェットにEditTextがある。このEditTextはデータの入力UIとして重宝するのだが,これがまた癖がある。
例えば,素の状態であれば入力した内容をそのままデータとして流用できない。別のButtonなどのUIを用意し,そちらの選択時に処理する必要がある。
表示領域の都合などで,処理用のボタンを設置したくない場合がよくある。例えば,EditTextを使ったTextEditorを作る場合なども困る。
TextWatcherを使うことで入力した内容をそのままデータとして活用できる。そこで,TextWatcherによる編集可能なEditTextの実装方法を整理する。方法
EditTextの公式リファレンス (EditText | Android Developers) に,以下のようにEditTextへの入力中の処理の実装方法が書かれている。
You also can receive callbacks as a user changes text by adding aTextWatcherto the edit text. This is useful when you want to add auto-save functionality as changes are made, or validate the format of user input, for example. You add a text watcher using theTextView#addTextChangedListenermethod.
TextWatcherをEditTextに追加することで,ユーザーによるテキストの変更を,コールバックで受け取ることができる。TextView#addTextChangedListenerメソッドの引数に,TextWatcherの実装を追加することで実現する。
TextWatcherはEditTextに入力中の内容を監視するインターフェイスとなっている。テキストの入力時に呼ばれるコールバックは,TextWatcherのリファレンスに記載されており,タイミングに応じて以下の3の抽象メソッドが用意されている。
| メソッド | 説明 |
|---|---|
abstract void afterTextChanged(Editable s) | テキストの変更後に呼ばれる。 |
abstract void beforeTextChanged(CharSequence s, int start, int count, int after) | テキストの置換前に呼ばれる。start: 先頭からの位置count: 変更前の選択文字数after: 変更後の選択文字数 |
abstract void onTextChanged(CharSequence s, int start, int before, int count) | テキストの置換後に呼ばれる。start: 先頭からの位置before: 変更前の選択文字数count: 変更後の選択文字数 |
beforeTextChangedは入力の反映前に呼ばれる。onTextChangedは入力の反映後に呼ばれる。onTextChangedとafterTextChangedはタイミングが似ている。
テキストの入力直前に何か処理を行いたい場合,beforeTextChangedのタイミングでやる。入力直後のタイミングであれば,onTextChangedとafterTextChangedのどちらでもいい。afterTextChangedの中で文字を追加すると,また
afterTextChangedが呼ばれる。このタイミングを検知する場合に,onTextChangedが必要になる。それ以外は
afterTextChangedでいいだろう。
仮引数に同じ変数名のcountが使われているが,意味が違うことに注意する。
引数の並び順が,入力欄の先頭からの位置 (start),変更前の選択文字数 (count/before),変更後の選択文字数 (after/count) の順番に並んでいると理解すると覚えやすいかもしれない。
例えば,2文字選択して1文字入力する場合count, after, beforeはそれぞれ以下となる。
beforeTextChanged:count=2,after=1onTextChanged:before=2,count=1
入力直後に何か処理を行いたい場合がほとんどなので,基本的にafterTextChangedのみ処理を実装することになるだろう。その場合,beforeTextChangedとonTextChangedの中身は空となる。
implements TextWatcherを記入し,オーバーライドする。サンプル
EditTextの内容をTextViewに表示する簡単なサンプルを以下に掲載する。GitHub上にも公開している。
なお,Android 3.5.3で確認した。
package jp.senooken.android.edittextwithtextwatcher;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText et = findViewById(R.id.editText);
et.addTextChangedListener(new MyTextWatcher());
}
private class MyTextWatcher implements TextWatcher {
private final TextView afterTextChanged_ = findViewById(R.id.afterTextChanged);
private final TextView beforeTextChanged_ = findViewById(R.id.beforeTextChanged);
private final TextView onTextChanged_ = findViewById(R.id.onTextChanged);
@Override
public void afterTextChanged(Editable s) {
String input= s.toString();
afterTextChanged_.setText(input);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
String input= "start=" + start
+ ", count=" + count
+ ", after=" + after
+ ", s=" + s.toString();
beforeTextChanged_.setText(input);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String input= "start=" + start
+ ", before=" + before
+ ", count=" + count
+ ", s=" + s.toString();
onTextChanged_.setText(input);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/afterTextChanged" />
<TextView
android:id="@+id/afterTextChanged"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/beforeTextChanged" />
<TextView
android:id="@+id/beforeTextChanged"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/onTextChanged" />
<TextView
android:id="@+id/onTextChanged"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autofillHints="number"
android:ems="10"
android:inputType="number"
android:hint="@string/hint"
tools:targetApi="o" />
</LinearLayout>
<resources>
<string name="app_name">EditTextWithTextWatcher</string>
<string name="hint">Fill this.</string>
<string name="afterTextChanged">afterTextChanged: </string>
<string name="beforeTextChanged">beforeTextChanged: </string>
<string name="onTextChanged">onTextChanged: </string>
</resources>
EditTextに入力した内容を3の抽象メソッドに対応したTextViewに引数と共に表示させている。
これで,beforeTextChangedとonTextChangedの引数の意味について理解できる。
結論
EditTextに入力された内容をTextWatcherで処理する方法を整理した。
EditTextを扱う上でほぼ必須の処理なので,整理できてよかった。地味に,公式リファレンスのbeforeTextChangedとonTextChangedの引数の意味がわかりにくくて,これの意味を理解するのに時間がかかった。
やはり自分で簡単な動作するサンプルを作成してみるのが,手間はかかるものの効果的だと感じた。
今後も,サンプルを作りながら理解を深めていきたい。

“Android:
TextWatcherによる編集可能なEditText” に対して1件のコメントがあります。