ticktakclockの日記

技術ポエムを綴ったりします。GitHub idも同じです (@ticktakclock)

Shared Elementで画面遷移にアニメーションを加える

こんにちは、tkyです。

Androidで動きのある画面遷移に挑戦してみようと思います。

画面間遷移にアニメーション

今回は画面から画面の遷移にSharedElement(これって機能名になるんですかね?)を指定して

「遷移元のUIの一部が遷移先のUIの一部になるように見せる遷移」

を実現してみたいと思います。

文面だけではかなり想像がつきにくいのでSharedElementしない画面遷移と対応した完成形イメージです。

対応前 対応後
f:id:ticktakclock:20200628165626g:plain
対応前
f:id:ticktakclock:20200628164953g:plain
完成形

github.com

Activitiy -> Activity のSharedElement

今回やったのはActivitiy -> Activityの画面遷移です。 他にはFragmentがあると思いますが、別の記事にしようと思います。

  • Fragment -> Fragment

僕自身どうやって画面遷移を実現してるのがあまり理解していなかったのでざっくりメモ含めて解説していきます。

順序としてはこんな感じのことをやればSharedElement実現できます。

この3つをこのコミットで対応していますので、差分だけ見るとより理解しやすいかもしれません。

github.com

  • startActivity時に遷移元のViewを指定
  • 遷移先のViewを指定
  • 遷移中のTransitionを指定

https://developer.android.com/training/material/images/SceneTransition.png

引用元:https://developer.android.com/training/transitions/start-activity

上記引用元はAndroidXで書かれていないので、参考にしつつAndroidX対応で書いていきます。

遷移元のViewを指定

アニメーションさせたいViewを指定します。第3引数のStringは後に出てくる遷移先のViewで指定するStringと同じものにする必要があります。

遷移元でやることはこれだけです。

val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this@MainActivity, view, "photo")
ActivityCompat.startActivity(
    this,
    intent,
    options.toBundle()
)

遷移先のViewを指定

最終的に到達したい場所のViewを指定します。この場合ImageView->ImageViewの遷移です。 onCreate() 等でレイアウトをInflateしたあとに遷移させたいViewを指定します。この時setTransitionNameの第2引数は遷移元のViewで指定したStringと同じものにします。

ViewCompat.setTransitionName(binding.imageView, "photo")

ハマったところ

ここで1つハマったところがあって画像は基本的にURLがあってロードしてあとから表示の流れになると思います。

こうなるとImageViewの高さがあとから決まるのでwrap_contentとかにしていると高さ0のImageViewとかになってしまいます。

その高さ0のViewめがけてアニメーションしてしまうのでちょっと変な動きになります。下の動画で「ペスカトーレ」「ジャンバラヤ」をタップした時にViewが上に吸い込まれるような挙動になってしまったりします。

f:id:ticktakclock:20200628182530g:plain
微妙にうまく動いていない

遷移先のViewは高さを指定するなどしてレイアウト時点でどこに配置されるべきかちゃんと定義する、を意識するのが良いのかもしれません。

遷移中のTransitionを指定

res/transition/change_image_transform.xml (名前は何でも良いです)に遷移中どのようにViewを移動させるかを定義していきます。

それぞれのChangeBounds、ChangeImageTransformについてはあまり調べていません。

(transformで位置(x, y)を変更、boundsで大きさ(w, h)変更ですかね?この2つを組み合わせてアニメーションします)

<?xml version="1.0" encoding="utf-8"?>
<transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
    <changeBounds />
    <changeImageTransform />
    <targets>
        <target android:excludeId="@android:id/statusBarBackground" />
        <target android:excludeId="@android:id/navigationBarBackground" />
    </targets>
</transitionSet>

まとめ

  • Activity -> Activity の画面遷移でSharedElementを使ってみた
  • 直線的なレイアウトは結構簡単にできた
  • Fragmentの画面遷移もトライしてみたい

参考にしたURL

Start an activity using an animation  |  Android Developers

Shared Element Activity Transition | CodePath Android Cliffnotes