Activity transitions+shared elementsで画面遷移にアニメーションつけてきた+詰まったところ

今携わっているマッチングアプリで、ActivityTransactionで画面遷移をやってみました。

poi_transition_success.gif

※こちらはデモなので私が撮影した我が家のかわいいネコチャンが表示されています。実際のアプリではかわいい女性が表示されます。


要件


  • カード選択画面(左右スワイプで選ぶ)→プロフィール詳細への移動のとき、同じ写真はスムーズなアニメーションで地続きになるようにする

  • プロフィール詳細には複数写真があるので、プロフィール詳細で選んだ写真がそのままカード選択画面でも表示されるようにする


参考サイト

Android公式サイト

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

Googleデベロッパーブログ

https://developers-jp.googleblog.com/2018/04/continuous-shared-element-transitions.html

その他transition animationやらいろいろ検索ででてきたサイト


実装方法


カードからプロフィールへの遷移


SharedElementsを有効にする

style.xmlで定義してあるアプリのrootThemeに

<item name="android:windowActivityTransitions">true</item>

<item name="android:windowSharedElementsUseOverlay">true</item>

を足す。


共有させたいObjectに同じtrasitionNameを指定

カード内のImageViewとプロフィール上部のImageViewのlayoutのxmlにtrasitionNameで同じ名前を指定します。

<ImageView

android:id="@+id/profile_photo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:transitionName="share_profile_photo_img"/>

これはプロフィール側の指定ですが、遷移元のカード側も同様にandroid:transitionName="share_profile_photo_img"を足します。

直接書くよりも、strings.xmlに引数切った方が共有しやすいと思います。


startActivityにActivityOptionsを足す

遷移タイミングのstartActivityのBundleにoptionsとしてActivityOptionsを足す。

public void startActivity (Intent intent, Bundle options)

↑に↓を足す。

https://developer.android.com/reference/android/app/ActivityOptions.html#makeSceneTransitionAnimation(android.app.Activity,%20android.util.Pair%3Candroid.view.View,%20java.lang.String%3E...)

実際のコードでは、

ActivityOptions.makeSceneTransitionAnimation(requireActivity(),android.util.Pair.create(view.photo, "share_profile_photo_img"))

をstartActivityの第二引数に渡しています。


遷移先のActivityでImageのローディングをする

↑のstartActivityのタイミングで表示するurlを渡しておきます。(実際の実装だと諸事情によりUrlのlistと選択しているurlを渡しています)

で、onCreateでsetContentView(view)後に、対象のImageViewにローディングをします。

transition_load.gif

このとき、ただロードするとロードよりも遷移が先にきてしまい、画像が用意されていないので背景の白色が一瞬うつってしまいます。

なので、ロード前に遷移を止めるActivity#postponeEnterTransition()を呼び出し、画像ロード後にActivity#startPostponedEnterTransition()を呼び出すと、白のチラツキなしに遷移ができるようになります。

https://developer.android.com/reference/android/app/Activity.html?hl=ja#postponeEnterTransition()

https://developer.android.com/reference/android/app/Activity.html?hl=ja#startPostponedEnterTransition()

多分よくある失敗がロードが失敗したタイミングで呼び出し忘れて永遠に遷移しない、だと思うのでわすれずに呼び出しましょう。


引っかかったポイント


画像がなめらかに移動せず、拡大してからちょうどいいサイズになるように見える

poi_transition_fail.gif

最初実装したときはこんな感じでした。一回画像が拡大してから移行しているように見え、スムーズじゃないですね。

原因としては、画像の拡大率。どちらのImageViewもandroid:scaleType="fitCenter"を設定していましたが、カード側が縦長、プロフィール側が横長でした。

なので、プロフィール側の画像サイズを必ず縦長にするようにして、回避しました。


プロフィールで画像を切り替えたときにカード側に伝える方法

これはActivityTransitionに直接関係ないのですが、Transitionで遷移→戻るのタイミングで、遷移を止めるActivity#postponeEnterTransition()のようなものが見当たりませんでした(あったら教えてほしい)

で、onActivityResultをつかってみたのですが、アニメーション遷移で遷移するタイミングで間に合わず、直前に表示されていた画像がチラついていました。

対応としては、EventBus的な機構を使って、カード側のFragmentをonCreate→onDestroyまでイベントを待機して、切り替えるたびに親画面に伝え、画像のIndexを切り替え、再表示タイミングでそのurlで画像を呼び出すとだいたいキャッシュされているのですぐに表示されるため、それでしのいでいます。

正直あんまりいいやり方ではないので、他にいいやり方があれば知りたいです。


簡単でしょ?

簡単じゃなかっ……

前述のデベロッパーブログに乗っているソースコードを読みつつ、適宜いろんなブログを見つつしてなんとかわかった感じです。

でも、これを入れるだけでなんかちょっと今どきのアプリ感は出るかなーと思うので、ぜひやってみてください!

poi_transition_success.gif