TextWatcherでViewを引数で取得したい
Androidでボタンをクリックした時などにコールバックを受け取るリスナーって
たいていの場合、どのViewのイベントかを判別できるようにViewのインスタンスを取得できる仕様になってると思います。
OnClickListenerの場合
override fun onClick(view: View?) {
when(view){
hoge1Button -> {
//処理
}
hoge2Button -> {
//処理
}
}
}
まさに上のような感じ。
引数にViewが入ってくるので、どのViewなのかで条件分岐してあげてそれぞれに処理を書くことができます。とても便利ですね。
しかし
なぜか、EditTextの文字列を取得するTextWatcherでは、それができない。
TextWatcherの場合
override fun afterTextChanged(p0: Editable?) {
//処理
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
引数にViewが入ってこない・・・。なんなんだこの仕様は・・・。
正直なところ、EditTextが一つだけならあまり問題にならないのですが
Activity内にEditTextが複数存在し、それぞれに処理を書かないといけないとなると
hoge1EditText.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(p0: Editable?) {
//処理
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
})
hoge2EditText.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(p0: Editable?) {
//処理
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
})
hoge3EditText.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(p0: Editable?) {
//処理
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
//処理
}
})
...
このような感じで、EditTextごとにTextWatherを実装してやらなきゃいけないので、めちゃくちゃソースが膨れ上がります。冗長で読みづらくなり保守性も下がっていきます。
そこで、何とかしてこいつらをまとめられるように、Viewを引数に取れる実装にできないか考えてみました。
まず、Viewを引数に取れるメソッドを定義した独自のinterfaceを作ります。
今回はとりあえずCustomTextWatcherListenerと命名しています。
interface CustomTextWatcherListener {
fun afterTextChanged(view: View, p0: Editable?)
fun beforeTextChanged(view: View, p0: CharSequence?, p1: Int, p2: Int, p3: Int)
fun onTextChanged(view: View, p0: CharSequence?, p1: Int, p2: Int, p3: Int)
}
次に、TextWatcherを実装したクラスを定義します。
コンストラクタでViewとCustomTextWatcherListenerを渡せるように実装しましょう。
クラスの名前は適当にCustomTextWatcherで
class CustomTextWatcher(val view: View, val listener: CustomTextWatcherListener): TextWatcher {
override fun afterTextChanged(p0: Editable?) {
listener.afterTextChanged(view, p0) //上で作成したリスナーに処理を渡す
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
listener.beforeTextChanged(view, p0, p1, p2, p3) //上で作成したリスナーに処理を渡す
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
listener.onTextChanged(view, p0, p1, p2, p3) //上で作成したリスナーに処理を渡す
}
}
最後に、上で定義したクラスとインターフェースを使って
Activity内に実装していきましょう。
class HogeActivity : Activity(), CustomTextWatcherListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hoge)
hoge1EditText.apply { addTextChangedListener(CustomTextWatcher(this, this@HogeActivity)) }
hoge2EditText.apply { addTextChangedListener(CustomTextWatcher(this, this@HogeActivity)) }
hoge3EditText.apply { addTextChangedListener(CustomTextWatcher(this, this@HogeActivity)) }
}
override fun afterTextChanged(view: View, p0: Editable?) {
when(view){
hoge1EditText -> {
//処理
}
hoge2EditText -> {
//処理
}
hoge3EditText -> {
//処理
}
}
}
override fun beforeTextChanged(view: View, p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(view: View, p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
}
TextWatcherでViewを含んだコールバックを受け取れるようになりました。
EditTextの数が多くなればなるほどこの実装は便利になるので、ぜひ活用してみてください。
間違っている箇所や、より良い実装がありましたらご指摘いただけると幸いです。