10
12

More than 5 years have passed since last update.

EditText を継承するときに気をつけること

Posted at

EditText を拡張し、独自のアトリビュートを宣言し、それを読み取って使うときに注意する点。

onTextChanged コールバックはコンストラクタから呼ばれる

EditText の親の TextView には、TextView#onTextChanged(CharSequence, int, int, int)というコールバックがあり、これを継承することで、TextWatcherを使わなくても、セットしている文字列の変更をモニタできます。

このコールバックは、通常TextView#setText()のような文字列の変更を行うメソッドの中で呼ばれますが、もう一つ、TextView を初期化するコンストラクタでも呼び出されています(Android 4.4.2 のコードベースではL3817あたり)。

オーバライドすることを許されたメソッドをコンストラクタで呼び出す危険性は EffectiveJava が説明している通りですが、もし継承によって拡張した独自の EditText が、TextView#onTextChanged(CharSequence, int, int, int)をオーバライドして、コンストラクタで初期化している(と思っている)はずのオブジェクトを使用していた場合、容易に NullPointerException なり何かしらの実行時例外が起こってしまいます。

// super のコンストラクタで onTextChanged が呼ばれてしまう
public class MyEditText extends EditText {
    private Something mSomething;

    public MyEditText(Context context) {
        super(context);

        init(context, null, 0);
    }

    public MyEditText(Context context, AttributeSet attrs) {
        super(context, attrs);

        init(context, attrs, 0);
    }

    public MyEditText(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        init(context, attrs, defStyle);
    }

    private void init(Context context, AttributeSet attrs, int defStyle) {
        mSomething = new Something();
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        super.onTextChanged(text, start, lengthBefore, lengthAfter);

        mSomething.doSomething(); // コンストラクタで呼ばれてしまうので、mSomething が null のまま
    }
}
10
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
12