[Android]ViewPager2で差分変更アニメーションを無効にする
はじめに
ViewPager2 を RecyclerView の代替として使った時に詰まった事例の解決方法を紹介します。
ViewPager2
ViewPager2 には RecyclerView.Adapter の使用が可能で、いい感じに横スクロールするリストを作る時に RecyclerView の代替として使うことができます。
binding.viewPager2.adapter = HogeRecyclerViewAdapter()
この時、 リストの差分変更アニメーションを無効にしたい場合があります。
いわゆる notifyItemChanged
によるチラつきを無くしたい場合などです。
RecyclerView の場合は手っ取り早くアニメーションを無効にする方法として itemAnimator
に null を代入したり、 itemAnimator
の特定のオプションだけを無効にする方法が使えます。
// 全アニメーション無効
binding.recyclerView.itemAnimator = null
// 差分変更アニメーションのみ無効
(binding.recyclerView.itemAnimator as DefaultItemAnimator).supportsChangeAnimations = false
しかし、 ViewPager2 では itemAnimator
を直接参照して設定を変えることができません。
binding.viewPager2.itemAnimator = null // NG!!
ViewPager2 で差分変更アニメーションをどう無効にすればいいのかで1日ぐらい悩みました。
解決方法
以下のように、 空の PageTransformer
を ViewPager2 にセットすればアニメーションが無効になります。
binding.viewPager2.setPageTransformer { _, _ -> }
特に空である必要はなく、とにかく何らかの値を渡してあげればいいです。
理由
ViewPager2.setPageTransformer
の内部コードを覗いてみます。内部は Java ですが、そんなに難しいコードは出てこないです。
public void setPageTransformer(@Nullable PageTransformer transformer) {
// ------- ↓注目 -------
if (transformer != null) {
if (!mSavedItemAnimatorPresent) {
mSavedItemAnimator = mRecyclerView.getItemAnimator();
mSavedItemAnimatorPresent = true;
}
// ------- ↓注目 -------
mRecyclerView.setItemAnimator(null);
} else {
if (mSavedItemAnimatorPresent) {
mRecyclerView.setItemAnimator(mSavedItemAnimator);
mSavedItemAnimator = null;
mSavedItemAnimatorPresent = false;
}
}
...
}
上記の // ------- ↓注目 -------
とした箇所に着目してみてください。
if (transformer != null) {
...
mRecyclerView.setItemAnimator(null);
}
setPageTransformer
の引数である transformer
が null でない場合、 mRecyclerView.itemAnimator
に null がセットされています。
つまり、とにかく ViewPager2.PageTransformer
に null 以外の値をセットしてあげれば ViewPager2(をRecyclerViewとして使った場合) のアニメーションが無効になるのです。
処理としてはたったこれだけです。
おわりに
個人的にも 果たしてこれが本当にベストプラクティスなのか? という疑問の残る内容ではありますが、自分と同じように困っている人のために記事を残しておきます。
また、もっとスマートなやり方をご存知の方がいらっしゃいましたら情報提供ぜひお待ちしております。
Discussion