🗃️

Jetpack Composeで画面遷移アニメーションしたいときに役立つであろう情報詰め合わせ

2023/03/05に公開

余談

Androidなどモバイルアプリ開発ではところどころにアニメーションを入れてあげることでUXを大きく向上させることができます。
そこで画面遷移を行うときにもアニメーションを入れたい。
「デフォルトのフェードじゃなくて、もっとかっこいい感じで!」
そんなときに役立つであろう知識(私が実際に開発している中で感じたことなども含め)を今回は詰め合わせてみました。

まずどうやってアニメーションするの

現状ComposeでNavigation(画面遷移時)をアニメーションさせるには、まだ試験的なAPIとなる、 Accompanistが必要です。
おそらくGoogleが直接提供しているもので、私が使った感じではあんまり問題はなさそうでしたが、実際にアプリに使用する際はよく検討した上で行ってください。

Accompanistを依存関係に追加

appの方のbuild.gradleのdependencies内に追記しましょう(新しいバージョンがあったら警告が出るのでそれに従ってアップデートしましょう)。
(冒頭の+は要りませんよ)

+    //Animated Navigation
+    implementation "com.google.accompanist:accompanist-navigation-animation:0.24.5-alpha"

まず必要なのはAnimatedNavControllerです。画面を切り替えたいとき、このコントローラで操作したりします。リモコンでチャンネル変えるみたいな感じ。
rememberAnimatedNavController()で作成して、変数に入れておきます。

val con = rememberAnimatedNavController()

Composeでアニメーションを書きたいとき、画面遷移で切り替えたい部分(常に同じ画面が表示されているわけでない部分)はNavHostというComposableを置いておきます。
NavHostが額縁で、そのなかの絵(Composable)をNavControllerで切り替えるってことですね。

AnimatedNavHost( //ここではanimationをつけたいのでAnimatedNavControllerを使う。
  navController = con, // 上で作ったAnimatedNavControllerを引数として渡す(ここでエラーが出る方はNavControllerの定義場所とスコープを確認)。
  startDestination = "screen1", // 初めに表示させたい画面の名前を考えて渡す(文字列です。あとで定義します)。
) {
  // compose関数でComposableを読ませます
  composable(
    "screen1" // 画面の名前を考えて渡す(文字列として)
  ) {
    // ここから先は一個目の画面のデザイン
    Column {
      Text(text = "画面1")
    }
  }
  composable(
    "screen2"
  ) {
    // ここから先は2こめの画面のデザイン
    Column{
      Text(text = "画面2")
    }
  }
}

NavControllerを用いて画面遷移をしてみましょう。
変数conNavController(AnimatedNavController)が入っている状況では、con.navigate("<画面名>")としてnavigate()関数を使ってあげることで、画面を切り替えられます。

AnimatedNavHost(
  navController = con,
  startDestination = "screen1",
) {
  composable(
    "screen1"
  ) {
    Column {
      Text(text = "画面1")
      // ここ
      Button(onClick = {con.navigate("screen2")}) {
        Text(text = "画面2へ")
      }
    }
  }
  composable(
    "screen2"
  ) {
    Column{
      Text(text = "画面2")
      // ここ
      Button(onClick = {con.navigate("screen1")}) {
        Text(text = "画面1へ")
      }
    }
  }
}

これでとりあえず画面遷移は実装できるはずです。
何も表示されない方や、一度Navigationを実装したいと思ったけど諦めて、その後に実装した方などは、「すでに実装している場合」の操作で直る可能性があります。
治らない場合は、このAPIは試験運用バージョンなためアップデートにより扱いが変わった可能性があります。最新の情報を見て自分で実装してみてください。

次の操作を行ってください。
Android Studioで文字列置換はCtrl + rまたはCtrl + Shift + rで行えます。

  • rememberNavController()rememberAnimatedNavController()に変更
  • NavHostAnimatedNavHostに変更
  • import androidx.navigation.compose.navigationを完全に消し去って(これを消さないと何も表示されなくなってハマります)、import com.google.accompanist.navigation.animation.navigationに変更
  • import androidx.navigation.compose.composableを完全に消し去って(上と同じく必ず消しておきましょう)import com.google.accompanist.navigation.animation.composableに変更

これでとりあえず既存のナビゲーションをアニメーションありで画面遷移をする準備は完了です。
変更する前と同様に、きちんと要素が表示され、画面切り替え時にフェード効果が付いていることを確認してください。

実際にアニメーションを弄ってみる!

ここまでの準備を終えたらいよいよComposeeでも複雑な画面遷移アニメーションを行えます!(やった!)
やり方を詳しく見てみましょう👀

画面遷移アニメーションを追加する

AnimatedNavHostの中、compose関数を使ってComposableを呼び出しているところに、アニメーションを書いていきます。
ここです。
アニメーションを実装する場所(引き)
compose関数の引数にアニメーション定義するよ
そうです。compose関数の引数としてアニメーションを定義できます。
ここで、定義できるアニメーションは次の4つです。

  • enterTransition
    画面遷移して表示されるときのアニメーション
  • exitTransition
    画面遷移して消えるときのアニメーション
  • popEnterTransition
    別の画面から戻るボタンなどを使用して、この画面に戻るときのアニメーション
  • popExitTransition
    戻るボタンなどを使用してこの画面から前の画面に戻るときのアニメーション

それぞれ引数として処理を渡すことができるので、enterTransitionで試してみましょう。
使えるアニメーションは次のサイトのとおりです。
EnterTransition と ExitTransition の例
ここで重要なのは、〜In系のアニメーションはenterTransitionのみ、〜Out系はexitTransitionのみなどという制限があることです。
適切なアニメーションを選んでください。

composable(
  "screen2",
  enterTransition = {
    scaleIn(
      initialScale = 0.9F,
      transformOrigin = TransformOrigin.Center
    )
  }
) {
  // 画面内容のComposable
}

今回はscaleInという大きさが変わって登場するアニメーションを使ってみました。
screen2に切り替わるときのアニメーションをつけたかったので、screen2のcomposable関数の引数にenterTransitionを指定します。
scaleInのドキュメントはこれです。
https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#scaleIn(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Float,androidx.compose.ui.graphics.TransformOrigin)
変数で今回指定しているのはinitialScaletransformOriginの2つです。

  • 前者は最初の大きさで、今回は0.9なので0.9倍から1倍に大きくなって登場します。
  • 後者は大きくなるとき、どこ基準で大きくなるかということ。今回はTransformOrigin.Centerなので、画面中央から拡大されます。
    使えるアニメーションとその引数は、このページにまとまっているので、これを見ながら実装しましょう。

https://developer.android.com/jetpack/compose/animation?hl=ja#enter-exit-transition
アニメーションの名前をクリックすると引数などの情報も見れます。

本題:気づいた便利な情報集

ここから先はNavigationをアニメーションさせる上で気づいた便利な小ネタ的なことを紹介します。
とても実用性はあるはずなのでぜひ期待してみていってください。

アニメーションは組み合わせられる

例えば、透明度を変えながら大きさも変えて登場したいとき、ありますよね?(あるよね)
そんなときはなんと!Transitionの足し算ができます。やってみましょう:

composable(
  "screen2",
  enterTransition = {
    scaleIn(
      initialScale = 0.9F,
      transformOrigin = TransformOrigin.Center
    ) + fadeIn() //ここ!!!!
  }
) {
  // 画面内容のComposable
}

できた!(GIF画像できたら貼る)
fadeで透明度を変えつつ、大きくなって登場しています。
これで良い感じに画面遷移できますね。かっこいい。

アニメーションは秒数指定できる

何秒かかって登場してほしいとか、ありますよね。
ただこれはかけすぎると非常にUXが低下するので、0.3秒あたりくらいがおすすめです。
では秒数指定する方法を見ていきましょう。

composable(
  "screen2",
  enterTransition = {
    scaleIn(
      initialScale = 0.9F,
      transformOrigin = TransformOrigin.Center,
      //ここ!!!!
      animationSpec = tween(
        durationMillis = 5000
      )
    ) + fadeIn()
  }
) {
  // 画面内容のComposable
}

これで画面が5秒かけて大きくなっていきます。
詳しく見ていきましょう

アニメーションscaleInにanimationSpecという変数に、tweenという関数を渡しています。
このtweenに関してはここでは触れられませんが、そのtween関数にdurationMillisとして、何ミリ秒かけるかというアニメーションの情報を渡しています。
単位はミリ秒なので、1秒が1000、0.5秒が500といった感じになります。

アニメーションにイージングがかけられる

加速度を感じられる画面遷移は、とてもアプリの雰囲気を良くします。
映像制作をやったことがある方はグラフで図形やテキストの動きに加速減速をつけたことがあるでしょう。
それがComposeによる画面遷移でもできるわけです。
イージングありなしでの比較やUIでの活用などはこの記事が面白いです。
https://note.com/ritar/n/n5e8ed0e07917
映像でのイージングはこの動画が面白いです。
https://www.youtube.com/watch?v=8NXQKc4NjoY
https://www.youtube.com/watch?v=YzlIBkHuWzM

やりかた

簡単です。
さっきと同じようにanimationSpec引数にtween関数の戻り値を渡してあげます。
そして、tween関数にeasing引数を渡して、イージングの種類を教えてあげるというところですね。

composable(
  "screen2",
  enterTransition = {
    scaleIn(
      initialScale = 0.9F,
      transformOrigin = TransformOrigin.Center,
      animationSpec = tween(
        easing = EaseOutQuint
      )
    ) + fadeIn()
  }
) {
  // 画面内容のComposable
}

イージングの種類はこのサイトを見てやってみてください。
https://easings.net/

animationSpecでもっといろんなアニメーションができる。割と可能性が無限大

上の2つでtween関数を使ってアニメーションをカスタマイズしましたが、物理学ベースのアニメーションとか、バネみたいなアニメーションとか、キーフレーム打ってアニメーションとかまでできるみたいなので、animationSpecを返す関数系の関数がたくさん乗っているドキュメントを貼っておきます。
https://developer.android.com/jetpack/compose/animation?hl=ja#customize-animations

GitHubで編集を提案

Discussion