【android】EditTextのsetText()が効かない&反映されない!

製作 プログラム

2017/07/07(金)

androidでEditTextのsetText()が反映されない問題

EditTextって他のViewと違ってチョット特殊ですよね。画面を回転させても(画面回転させると再描画されてが画面が初期化されます)、それにアプリを一度閉じたり、アクティビティを切り替えても初期化されず入力した文字が保持されます。

EditTextの中身だけ保持されるので、アンドロイドアプリを開発している時に「ナゼなんだ!」と疑問に思った人も多いと思います。

かく言うぼくも今回EditTextの表示テキストを変えようとsetText()で初期化していたのですが、なぜかうまく反映されず設定前のテキストがそのまま表示されるようになってしまい、見事にEditTextの罠にはまってしまいました。

今回はそもそもなぜEditTextだけが特殊なのか、どうやったら初期化がうまくできるのかを紹介しますね。

そもそもナゼEditTextは特殊?

まずはボタンやTextViewと違うEditTextの特殊なトコロを紐解いていきましょう。

アンドロイドアプリでEditTextは主に名前を入力したり文章を入力したりと、フォームの部品に使われていると思います。

他のView(ボタンなど)は基本的にソースコードをバリバリ書いてsetText()を呼び出さない限り表示テキストが変わらないのが普通ですが、EditTextだけはユーザーがポチポチ入力しただけで表示テキストが変わるというコトになります。

そのため、なんとアンドロイドの基幹ソースコードでEditViewだけは、アプリが非アクティブになるタイミングでonSaveInstanceState()を自分で呼んで、入力テキストを保存しているのです。

そして保存された入力テキストはOnCreate()の後で呼ばれる、onRestoreInstanceState()内で再び空のEditTextに自動的に設定されています。

なんかとても優秀ですよね、EditTextって。

EditTextは自分で入力テキストを保存して、自分で再設定しているすごいヤツ!

保存されるタイミングは?

保存するのは非アクティブになるとき、具体的にはアクティビティ、フラグメント共通でonSaveInstanceState()が呼ばれるタイミングで保持されてます。

※onSaveInstanceState()はonCreate()みたいにライフサイクルをつかさどっているメソッドです。もちろんonCreate()と同じでオーバーライドして処理を記述するコトもできます。

再設定されるタイミングは?

次にEditTextに入力テキストが復元されるタイミングですが、コレがちょっと厄介です。

アクティビティの場合
onCreate()→onStart()→onRestoreInstanceState()→onResume()
フラグメントの場合
onCreate()→onCreateView()→onActivityCreated()→※restoreViewState()→onStart()→onResume()

上記にアクティビティとフラグメントそれぞれのライフサイクル図を抜粋してみました。それぞれ色つきのトコでEditTextの中身が再設定されてます。

まずアクティビティの場合はそのものずばりのライフサイクルのメソッド「onRestoreInstanceState()」というのがあり、その中でEditTextの表示テキストが再設定されています。

次にフラグメントの場合はライフサイクル上に「onRestoreInstanceState()」というのが存在しないので話がややこしくなっています。

restoreViewState()で再設定されているのですが、このメソッドはライフサイクルをつかさどるメソッドじゃないのでオーバーライドとかができません。アンドロイドの基幹ソースコードで内部的に呼ばれています。

該当するアンドロイド基幹ソースコードの抜粋です。

http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/app/FragmentManager.java815行目付近

 f.onActivityCreated(f.mSavedFragmentState);
if (!f.mCalled) {
  throw new SuperNotCalledException("Fragment " + f
      + " did not call through to super.onActivityCreated()");
}
if (f.mView != null) {
  f.restoreViewState();
}
f.mSavedFragmentState = null;

抜粋の先頭行で「onActivityCreated()」が呼ばれていてその数行下で「restoreViewState()」が呼ばれているコトがわかります。

EditTextの中身が復元されるタイミングはアクティビティとフラグメントで異なる!

結局いつ初期化すればいいの?

というコトでEditTextを初期化するにはアクティビティの場合ならonResume()でsetText()をしてあげればいいというコトがわかりました。

さらにフラグメントの場合だとonStart()かonResume()でsetText()してあげれば無事初期化できます。

アクティビティでもフラグメントでもEditTextはとりあえずonResume()で初期化してたら問題なし!

まとめ

今回は画面回転や非アクティブ時でも値を保持し続けるというViewの中でもかなり特殊な特徴を持つEditTextについて、原因から対策までいろんな方面から紹介しました。

アンドロイドでアプリを開発しているとき、アクティビティだとonCreate()、フラグメントだとonCreateView()で画面の初期化をするコトが多いと思います。(僕自身アンドロイドアプリ開発がはじめてなのでホントはこの考え方自体が間違っているのかもしれません。)

ただEditTextに限ってはそのタイミングで初期化しても「setText()→EditText自身の再設定処理」となってしまい、うまいこと初期化ができず初期化前の入力テキストが表示され続けちゃいます。

onResume()、フラグメントだったらonStart()以降でsetText()してあげましょうね!(ただこうすると今度は逆に画面回転時とかに表示テキストがリセットされちゃいますが…。別途画面回転を禁止するなどして対策しないといけません。)