RecyclerViewへアイテムが表示されるとき、アニメーションはありません。。一瞬で表示されて終わりです。
「RecyclerViewへアイテムが表示される」ことを、ここでは「アイテムの出現」と言い表すことにします。
このアイテムの出現にアニメーションを付ける方法を紹介します。
アイテムの出現をアニメーションで演出することで、RecyclerViewに表示したい内容が際立つと思います。
目次
出現アニメーションとは
アイテムの出現アニメーションとは次のようなものです。
この出現アニメーションを実現するためにLayoutAnimationという仕組みを使います。
LayoutAnimation
LayoutAnimationとは
LayoutAnimationはViewGroup(コンテナタイプのView)が持つアニメーションの仕組みです。ViewGroup内の子Viewが表示される時にViewAnimationを付けて表示します。
Constraint・Linear・FrameLayoutやRecyclerViewは全てViewGroupのサブクラスなので、LayoutAnimationの適応が可能です。
例えば、ConstraintLayoutに「拡大しながら不透明になる」アニメーションを付けると次のようになります。
ImageViewが中央から広がる表示です。若干の時間差があり、左上⇒右上⇒左下⇒右下の順番でアニメーションが進んでいます。
静的な記述方法(xmlに記述)
LayoutAnimationの静的な記述方法は、xmlへ記述してリソースとして扱う方法です。
ViewGroup(ConstraintLayout)のlayoutAnimation属性へ、xmlで定義したLayoutAnimationを指定するだけです。
アニメーションのxmlはres/animフォルダ以下に配置する決まりになっています。
... <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/layBlock" android:layoutAnimation="@anim/block_appearance" ...> <ImageView android:id="@+id/imgA" android:background="@android:color/holo_blue_dark" ... /> <ImageView android:id="@+id/imgB" android:background="@android:color/holo_green_dark" ... /> <ImageView android:id="@+id/imgC" android:background="@android:color/holo_orange_dark" ... /> <ImageView android:id="@+id/imgD" android:background="@android:color/holo_red_dark" ... /> </androidx.constraintlayout.widget.ConstraintLayout> ...
<?xml version="1.0" encoding="utf-8"?> <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:animation="@anim/scale_up" android:delay="15%" android:animationOrder="normal"/>
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@android:integer/config_mediumAnimTime"> <scale android:fromXScale="0%" android:toXScale="100%" android:fromYScale="0%" android:toYScale="100%" android:pivotX="50%" android:pivotY="50%"/> <alpha android:fromAlpha="0.0" android:toAlpha="1.0"/> </set>
LayoutAnimationの属性は表に示した意味があります。必要に応じて変更すれば出現の様子が変えられます。詳細はLayoutAnimationControllerのドキュメントを参照してください。
属性(android:XXX) | 動作/例 |
---|---|
animation | ViewAnimationを指定 |
android:animation="@anim/scale_up" | |
animationOrder | アニメーションを開始するViewの順番 normal:indexの昇順(デフォルト) random:ランダム reverse:indexの降順 |
android:animationOrder="normal" | |
delay | アニメーションを開始するタイミングの遅延 %:前Viewのアニメーションが?%進んだところでスタート %p:親Viewのアニメーションが?%進んだところでスタート |
android:delay="15.0%" | |
interpolator | 初期値~最終値までの補間方法のタイプ インターポレータのリソースID |
android:interpolator="@android:anim/decelerate_interpolator" |
ViewAminationは詳細を取り上げません。ViewAnimationのドキュメントを参照してください。(AlphaAnimation、AnimationSet、RotateAnimation、ScaleAnimation、TranslateAnimation)
動的な記述方法(プログラム中に記述)
LayoutAnimationの動的な記述方法は、プログラムで記述する方法です。
静的な記述方法と全く同じ出現アニメーションのプログラムです。
... <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/layBlock" ...> <ImageView android:id="@+id/imgA" android:background="@android:color/holo_blue_dark" ... /> <ImageView android:id="@+id/imgB" android:background="@android:color/holo_green_dark" ... /> <ImageView android:id="@+id/imgC" android:background="@android:color/holo_orange_dark" ... /> <ImageView android:id="@+id/imgD" android:background="@android:color/holo_red_dark" ... /> </androidx.constraintlayout.widget.ConstraintLayout> ...
... val _layBlock = findViewById<ConstraintLayout>(R.id.layBlock) // ViewAnimationの作成 val _scaleAnim = ScaleAnimation( 0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f) val _alphaAnim = AlphaAnimation(0.0f, 1.0f) val _animSet = AnimationSet(false).apply { addAnimation(_scaleAnim) addAnimation(_alphaAnim) duration = resources.getInteger( android.R.integer.config_mediumAnimTime).toLong() } // LayoutAnimationの作成 val _layAnim = LayoutAnimationController(_animSet).apply { delay = 0.15f order = LayoutAnimationController.ORDER_NORMAL } // LayoutAnimationの指定 _layBlock.layoutAnimation = _layAnim ...
プログラムで記述すると煩雑になるので、アニメーションを分離して管理できる静的な記述方法をお勧めします。
出現の順番(開始するViewの順番)
ViewGroup内に表示する子Viewはインデックス番号を持っています。
ViewGroupはインデックス番号の昇順に子Viewを表示していくので、出現アニメーションも自ずとその順番に開始されます。
ただし、出現アニメーションの順番はデフォルトの場合です。意図的な変更(animationOrderを使用)も可能です。
インデックス番号の付けられ方は、レイアウトxmlであれば記述順、プログラムであればViewGroup#addViewメソッドの実行順になります。
... <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/layBlock" ...> <ImageView <-- index:0 android:id="@+id/imgA" android:background="@android:color/holo_blue_dark" ... /> <ImageView <-- index:1 android:id="@+id/imgB" android:background="@android:color/holo_green_dark" ... /> <ImageView <-- index:2 android:id="@+id/imgC" android:background="@android:color/holo_orange_dark" ... /> <ImageView <-- index:3 android:id="@+id/imgD" android:background="@android:color/holo_red_dark" ... /> </androidx.constraintlayout.widget.ConstraintLayout> ...
出現の遅延(開始するタイミングの遅延)
animation:delay属性の値は図のような関係になっています。
delayはアニメーション時間の全体に対する割合です。
繰り返したい時
LayoutAnimationはViewGroup内の子Viewが表示される時にViewAnimationを付けて表示します。
この子Viewが表示されるタイミングは2つあります。
- (1)アプリが起動してViewGroupが表示された時
- (2)View#requestLayoutを発行して再表示した時
(1)のタイミングはLayoutAnimationによりアニメーションが実行されます。これは、今まで動画サンプルで見てきた通りです。
しかし、(2)のタイミングはアニメーションが実行されません。LayoutAnimationは1回のみ有効だからです。
これを2回以降も有効にしたい場合は、子Viewの表示が始まる前にViewGroup#scheduleLayoutAnimationを実行します。
... val _layBlock = findViewById<ConstraintLayout>(R.id.layBlock) findViewById<Button>(R.id.btnRelayout).setOnClickListener { _layBlock.requestLayout() // 再表示の依頼 _layBlock.scheduleLayoutAnimation() // LayoutAnimationのスケジュール } ...
ちなみに、RecyclerViewのアイテムを更新(全データの変更)する場合、Adapter#notifyDataSetChangedで通知を行いますが、内部でView#requestLayoutが実行されています。よって、同じことが言えます。
RecyclerViewへ組み込み
RecyclerViewはViewGroupのサブクラスなので、LayoutAnimationが使用できます。
RecyclerViewへランキングを表示することを想定して、出現アニメーションを使った演出を行ってみました。
ベストテン(昔々の音楽番組)世代の私としていは、ランキングと言えば下位から上位に向かって、下から積み上がっていくイメージです。
... // ↓ 「LayoutAnimationを指定」の部分のみをプログラムから行う rcySample.layoutAnimation = AnimationUtils.loadLayoutAnimation(this@MainActivity, R.anim.item_appearance) ...
<?xml version="1.0" encoding="utf-8"?> <layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" android:animation="@anim/slide_in_top" android:delay="20%" android:animationOrder="reverse"/>
<?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@android:integer/config_longAnimTime"> <translate android:fromYDelta="-50%p" android:toYDelta="0" /> <alpha android:fromAlpha="0.0" android:toAlpha="1.0" /> </set>
関連記事: