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>
関連記事:
