通常は意識する必要は無いと思いますが、RecyclerViewのViewごとに表示状態のログを取ったり、非同期処理の後片付けを行いたいなどの理由で、正確なライフサイクルを知りたい場合もあるかと思います。
まあ、そういう状況になったので調べましたというエントリーです。
ViewHolderのライフサイクルイベント
ViewHolder自身にはイベントを受け取る仕組みはありませんが、RecyclerView.Adapterがそれぞれのイベント受け取っているため、必要に応じてViewHolderへ伝えるように実装できます。
スクロール
RecyclerViewをスクロールしていった時に発生するイベントは以下のようになります。
セルの表示
onCreateViewHolder
ViewHolderを作成する。この時点ではViewTypeのみ確定でどのデータを表示するかは決まっていない。名前の通りリサイクルされるため、表示に必要な数+キャッシュ分しか作られず、作成済みのインスタンスが使い回される。
onBindViewHolder
ここで表示位置が確定し、その位置に対応するデータと紐付けて表示などを変更する
onViewAttachedToWindow
実際に表示される
セルが画面外に出る
onViewDetachedFromWindow
非表示になる
再利用開始
onViewRecycled
Detachされて即座にリサイクルされるわけではありません、再表示のためにしばらく保留されます。
画面外に出て十分に離れた場所までスクロールしたときに、リサイクルが行われます。
別のセルへの再利用
onBindViewHolder
リサイクルが行われ、別のセルの表示のために利用されます。表示する位置が指定され、そのデータを紐付け、表示を変更します。
onAttachedToWindow
新しいデータでViewが表示される
上記のような順序でライフサイクルイベントが発生します
データの変更
RecyclerViewでデータの変更が伴う場合、Viewすべての作り直しでは無く、更新されるポイントに絞って更新を行うのが通常です。操作が限定的な場合以外は、notifyItemXXXメソッドを使うのではなく、DiffUtilsやListAdapterを使うことが多いでしょう。この状況でデータ列に変更を加えた場合にどうなるかを調べました。
なお、ItemAdapterにて、アニメーションを行うために新しいViewHolderが作られてしまう場合があります。デフォルトのSimpleItemAnimator
の場合、以下のようにすることで、この挙動を抑制することができます。この状態で検証してみます。
(recyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
並べ替え
個々のデータは変化せず、順序が入れ替わった場合です。
(並べ替えの結果表示される要素が変化しない場合)何も呼び出されません。
ViewHolderが保持するViewの表示位置が変更されただけですから何も呼び出されません
要素の削除
データ列から要素を削除した場合です。
onViewDetachedFromWindow
onViewRecycled
削除されたデータに対応するViewが表示されている場合、onViewDetachedFromWindowとonViewRecycledがコールされます。
要素の追加
データ列に新しい要素を追加した場合です。
onCreateViewHolder
onBindViewHolder
onViewAttachedToWindow
ViewHolderが作成済みのキャッシュから使われる場合は、onCreateViewHolderが呼ばれないですが、
新規要素が表示された場合と同様です。追加されたことで画面外にで多要素がある場合は、スクロールと同様です。
コンテンツの変更
同一のデータであるが、内容が異なると判定される変化があった場合です。
onBindViewHolder
対象のViewHolderに対してonBindViewHolderが呼び出されますので、ここで表示コンテンツを書き換えます。
別データとの入れ替え
一部のデータが別のデータであると判定される内容に変化した場合です。
旧要素について
onViewDetachedFromWindow
onViewRecycled
新要素について
onCreateViewHolder
onBindViewHolder
onViewAttachedToWindow
扱いとしては、要素が削除されて別の要素が追加された、という扱いになるので削除と追加の挙動が発生します。
ViewTypeが異なるデータとの入れ替え
異なるViewHolderと入れ替えられる言うだけで、別データとの入れ替えと全く同じなので省略
まとめ
まとめると以下のようなライフサイクルになっています。
具体的な例で言えば、
表示の開始、終了をロギングする場合は、
onViewAttachedToWindow、onViewDetachedFromWindowを監視する
表示データが画像のURLでそのダウンロードを非同期に行う場合、
onBindViewHolderで開始
onViewRecycledか、次のonBindViewHolderでそのタスクのキャンセルを実行する
といった使い方になりそうですね。