表示・非表示アニメーションの必要性
アニメーションを実装するにあたり、大切なのは『なぜそのアニメーションを加えるのか?』その理由をしっかり理解しておくことです。
アニメーションの実行にはデバイスのCPU等のリソースを必要とするため、多かれ少なかれアプリのパフォーマンスに影響を及ぼします。
従って、アニメーションさせる必要がない状況では、無駄にアニメーションさせない方が良いと言えます。
一方で、アニメーションが効果的な場面で適切にアニメーションを利用することで、アプリの使い勝手や質をグッと高めることもできます。要は、使い所が肝心ということですね。
アニメーションを実装するためのコードを理解する前に、まずはこの前提をしっかり確認しておきましょう!
実のところ、コードを書いたり覚えたりするよりも、数段大事なポイントです。
では、今回紹介する表示・非表示のアニメーションはどのようなケースにおいて有効なのでしょうか?具体例を考えてみましょう。
たとえばToDo管理アプリでは、ユーザーが登録した項目を、分かりやすくリスト形式で並べて表示させる必要があります。
そして、それらの項目はユーザーによって削除される可能性もあるため、単にリストとして表示させるだけでなく、リストアイテムの表示・非表示を切り替えられるようにする仕組みが必要です。
しかし、ユーザーが削除のアクション(削除ボタンのタップなど)を起こしたのと同時に、ユーザーが確認する間もなく、削除されたリストアイテムが一瞬で非表示になったらどうでしょうか?
もしかしたら、ユーザーはどの項目が削除されたのかが分かりづらいと感じるかもしれません。
それに伴い、『もしかして間違って違う項目を削除してしまったのでは?』と、ユーザーが不安になってしまうおそれもあるでしょう。
これを防ぐには、表示状態から非表示状態への遷移をアニメーション化し、少し時間をかけてリストアイテムが非表示状態になるように設定することが有効です。
そうすれば、ユーザーは削除したリストアイテムを目で追って確認できるので、『間違いなく自分が削除したいデータを削除できた』と、確信することができます。
今回は簡易的なサンプルでアニメーション実装の説明を行うため、実際のアプリのようにデータを端末やサーバに保存・削除することはしませんが、本番ではアニメーションと共にデータのやり取りが行われるケースが多いことを念頭に置いておいてください。
次のステップでは、シンプルな表示・非表示の切り替えアニメーションをJetpack Composeを使って実装していきます!
POINT!
- ・アニメーションを加える前に、アニメーションの必要性を理解しておくことが大事!
- ・必要がないのであれば、無駄にアニメーションさせない方が良いケースも多い!
- ・表示/非表示の切り替えアニメーションは、リストアイテムの追加/削除の際によく利用される!
シンプルな表示・非表示のアニメーション
Jetpack Composeには、アニメーションを設定するための機能が標準で備わっています。(※gradle.ktsで設定されたライブラリのバージョンによって多少の違いはあります)
なので、これから紹介するアニメーションを実装するにあたり、依存関係は初期状態(Android Studioを立ち上げ、Empty Activityを選択してプロジェクトを開始した状態)のままでOKです。
NavigationやViewModelと違って、ライブラリを新しく依存関係に追加する必要がないのでお手軽ですね。
以上を踏まえた上で、まずはアニメーションが有効になっていない状態のコードを確認してみましょう。
下のコードは、ボタンのタップによって visible
の真偽値が切り替わり、if文を使った条件分岐によって要素(色で区別されたBox)を表示するかどうかを制御しています。
このコードは要素の表示・非表示という点に関しては期待通りの結果になりますが、表示状態と非表示状態の切り替わり方が瞬間的であり、アニメーションにはなりません。
@Composable
fun NoAnimation(modifier: Modifier = Modifier) {
// visible state
var visible by remember {
mutableStateOf(true)
}
Box(modifier = modifier.fillMaxSize()) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(Color.LightGray)
)
if (visible) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(MaterialTheme.colorScheme.primary)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(Color.LightGray)
)
Button(
// toggle visible state
onClick = { visible = !visible },
modifier = Modifier
.padding(top = 32.dp)
) {
Text(text = "Toggle Visible")
}
}
}
}
では、この表示状態の切り替わりをアニメーション化させるにはどうすれば良いのでしょうか?
方法はいくつかあるのですが、今回はシンプルで実装が簡単な方法をご紹介します。
表示・非表示の切り替えにアニメーションが必要な場合、AnimatedVisibility() を利用するのがオススメです。
AnimatedVisibility()
は、表示・非表示状態を切り替えるための真偽値を受け取り、AnimatedVisibility
の content
パラメータに渡されたコンポーザブルの表示・非表示を自動的にアニメーション化してくれます。
AnimatedVisibilityに関してより詳細な情報が必要な場合は、公式サイトのドキュメントを確認してください。
では、AnimatedVisibilityの最もシンプルな使い方を確認していきましょう。
先ほど紹介したコードでは、if文で条件分岐を判定し、要素の表示・非表示を制御していましたが、それをAnimatedVisibilityに任せます。
コードは次のようになります。
@Composable
fun AnimatedVisibilityEx(modifier: Modifier = Modifier) {
// visible state
var visible by remember {
mutableStateOf(true)
}
Box(modifier = modifier.fillMaxSize()) {
Column(
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(Color.LightGray)
)
// Enable to animate
AnimatedVisibility(visible = visible) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(MaterialTheme.colorScheme.primary)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(Color.LightGray)
)
Button(
// toggle visible state
onClick = { visible = !visible },
modifier = Modifier
.padding(top = 32.dp)
) {
Text(text = "Toggle Visible")
}
}
}
}
なんとこれだけで表示・非表示の切り替えがスムーズで自然なアニメーションになりました!
アニメーションに特にこだわりがない場合は、これでも十分だと思います。
むしろ、下手にアレンジを加えることでアニメーションが過度に派手になってしまい、返ってアプリの使い勝手が悪くなってしまうよりは、デフォルト状態のアニメーションの方が良いでしょう。
とは言え、『どうしてもアニメーションを変更したい・アレンジしたい』という声もあるかと思うので、次のステップでは少しアニメーションをアレンジしてみたいと思います!
POINT!
- ・当然ながら、if文を使った条件分岐だけでは要素の表示/非表示の遷移はアニメーションにならない!
- ・AnimatedVisibility()を利用すると、簡単に要素の表示/非表示の遷移をアニメーション化できる!
- ・AnimatedVisibilityには最低限、表示/非表示状態を制御するための真偽値を渡す必要がある!
アニメーションのカスタマイズ
AnimatedVisibilityには、真偽値を受け取る visible
の他にもパラメータが設定されており、enter
もしくは exit
パラメータに必要な値を渡すことで、アニメーションをカスタマイズすることができます。
enter
に渡す値で表示状態になる際のアニメーションを、exit
に渡す値で非表示状態になる際のアニメーションをそれぞれ制御することができます。
ここでは、exit
パラメータに必要な値を渡して、要素が非表示状態になる際のアニメーションをアレンジしてみましょう。
非表示状態になる際に、左方向へスライドアウトさせて、スーッと退場していくような感じにしてみたいと思います。
水平方向へスライドアウトさせるには、exti
パラメータに slideOutHorizontally()
を設定します。
ただし、slideOutHorizontally()
を設定しただけだと、スライドアウトした後も要素の高さが残った状態のままになってしまいますので、shrinkVertically()
を追加して、要素の高さもアニメーションになるようにします。
下は、アニメーションに関するコードだけを抜粋して記載したものです。
// Enable to animate
AnimatedVisibility(
visible = visible,
exit = slideOutHorizontally(
targetOffsetX = { -it },
animationSpec = tween(durationMillis = 300)
) + shrinkVertically(
animationSpec = tween(delayMillis = 300)
)
) {
// Animated Composable
}
上のコードのように、アニメーションの指定は +
演算子で繋げて指定することができます。
アニメーションは、slideOutHorizontally()
によってまず左方向へスライドアウトし、その後、shrinkVertically()
によって高さが元に戻る(縮む)アニメーションが実行されます。
動画で実際のアニメーションを確認したい方は、下の動画をご覧ください。
AnimatedVisibility()を利用することで、とても簡単にアニメーション化させることができる一方で、その制御やアレンジも行えるようになっているので本当に便利です。
ただし、序章で述べたように、必要のないアニメーションや過度なアニメーションはアプリのパフォーマンスに悪影響を及ぼし、返ってユーザビリティを損なうおそれがあるので注意が必要です。
アニメーションは、ここぞという時にさりげなく加えるのがオススメです!
POINT!
- ・AnimatedVisibilityのenter/exitパラメータに必要な値を渡すことで、アニメーションをカスタマイズできる!
- ・たとえば水平方向にスライドアウトさせたい場合は、slideOutHorizontallyを指定すればOK!
- ・高さの変化をアニメーションさせたい場合は、shrinkVerticallyも同時に指定しよう!