overridePendingTransitionがDeprecatedになったのでoverrideActivityTransitionに置き換える

`overridePendingTransition`がDeprecatedになったけど、`SDK_INT`で分岐させて`overridePendingTransition`を単純に`overrideActivityTransition`に置き変えればよい訳ではない。というお話

# 前置き

長らくActivity遷移アニメーションを指定するために使っていた `Activity#overridePendingTransition` がDeprecatedになってしまいました。

https://developer.android.com/reference/android/app/Activity#overridePendingTransition(int,%20int)

> This method was deprecated in API level 34.
Use overrideActivityTransition(int, int, int)} instead.

ということで `overrideActivityTransition` に置き換えなさいということですね。
`overrideActivityTransition` の説明には以下のように書かれています。

https://developer.android.com/reference/android/app/Activity#overrideActivityTransition(int,%20int,%20int)

> For example, if we want to customize the opening transition when launching Activity B which gets started from Activity A, we should call this method inside B's onCreate with overrideType = OVERRIDE_TRANSITION_OPEN because the Activity B will on top of the task. And if we want to customize the closing transition when finishing Activity B and back to Activity A, since B is still is above A, we should call this method in Activity B with overrideType = OVERRIDE_TRANSITION_CLOSE.

ようするに、AからBに遷移するときのアニメーションと、BからAに戻るときのアニメーションはBで指定しなさい。ってことらしいです。AからBに遷移するときのアニメーションはBのonCreateで指定しなさい。ってことです。
つまり、**overridePendingTransitionを単純にoverrideActivityTransitionに置き変えればよい訳ではない**ってことみたいです。

ちょっと検証してみましょう。

# 検証

遷移アニメーションが分かりやすいように以下のようなアニメーションリソースを定義しておきます。
<details><summary>アニメーションリソース</summary><div>

```slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromXDelta="-100%"
    android:toXDelta="0%"
    />
```

```slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromXDelta="100%"
    android:toXDelta="0%"
    />
```

```slide_out_left.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromXDelta="0%"
    android:toXDelta="-100%"
    />
```

```slide_out_right.xml
<?xml version="1.0" encoding="utf-8"?>
<translate
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:fromXDelta="0%"
    android:toXDelta="100%"
    />
```
</div></details>

`duration` を1000=1秒と長めに設定して、違いが分かりやすいようにしています。

# overridePendingTransition

MainActivityとMainActivity2の間で遷移させるとして、`overridePendingTransition`は以下のように使いますね。

```MainActivity.kt
binding.button.setOnClickListener {
    startActivity(Intent(this, MainActivity2::class.java))
    overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
}
```

```MainActivity2.kt
binding.button.setOnClickListener {
    finish()
    overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
}
```

こうすることで、MainActivityからMainActivity2の遷移とその戻りのアニメーションをカスタマイズすることができます。これはAndroid 14でもまだ使えますね。

|デフォルト|overridePendingTransition|
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/620eec10-9eee-b289-9317-ebbe79206bb5.gif)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/1c8e8dc3-17ee-6ad8-839b-0a1104fb92fc.gif)|

# 単純に置き換え

先に説明したとおり、単純な置き換えはできないということでしたが、置き換えた場合どうなるのかを見てみましょう。

```MainActivity.kt
binding.button.setOnClickListener {
    startActivity(Intent(this, MainActivity2::class.java))
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
        overrideActivityTransition(
            Activity.OVERRIDE_TRANSITION_OPEN, R.anim.slide_in_right, R.anim.slide_out_left
        )
    } else {
        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
    }
}
```

```MainActivity2.kt
binding.button.setOnClickListener {
    finish()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
        overrideActivityTransition(
            Activity.OVERRIDE_TRANSITION_CLOSE, R.anim.slide_in_left, R.anim.slide_out_right
        )
    } else {
        overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
    }
}
```

そうすると以下のように、`finish`のあととはいえ、MainActivity2の方で指定したクローズのアニメーションは適用されていますが、MainActivityの方で指定した、オープンのアニメーションは適用されませんでした。

|overridePendingTransition|overrideActivityTransition|
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/1c8e8dc3-17ee-6ad8-839b-0a1104fb92fc.gif)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/78a7372b-3099-05e1-0998-114f2f1882fd.gif)|

# 正しい対応方法

上に表示されるActivityのonCreateでOPEN/CLOSEとも指定するのが良さそうです。
CLOSEも指定するのは、`overridePendingTransition`と違って先に指定しておけるためであることと、後述しますが、予測型「戻る」ジェスチャーの挙動に影響するためです。
複数回呼び出した場合は上書きになるため、閉じ方によってアニメーションを変更したい場合はその直前に指定すれば変更できますし、`clearOverrideActivityTransition`という指定をクリアするメソッドも用意されています。

```MainActivity.kt
binding.button.setOnClickListener {
    startActivity(Intent(this, MainActivity2::class.java))
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left)
    }
}
```

```MainActivity2.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
        overrideActivityTransition(
            Activity.OVERRIDE_TRANSITION_OPEN, R.anim.slide_in_right, R.anim.slide_out_left
        )
        overrideActivityTransition(
            Activity.OVERRIDE_TRANSITION_CLOSE, R.anim.slide_in_left, R.anim.slide_out_right
        )
    }
    val binding = ActivityMain2Binding.inflate(layoutInflater)
    setContentView(binding.root)
    binding.button.setOnClickListener {
        finish()
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
            overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right)
        }
    }
}
```

|overridePendingTransition|overrideActivityTransition|
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/1c8e8dc3-17ee-6ad8-839b-0a1104fb92fc.gif)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/f853697e-9b79-ae7f-0262-fda5b3527fa5.gif)|

# 予測型「戻る」ジェスチャーの挙動

`overrideActivityTransition`で`OVERRIDE_TRANSITION_CLOSE`の指定をすると、Android 14から導入された、予測型「戻る」ジェスチャーでの動きにも適用されます。このアニメーションはfinishの前に実行されるものなので、このタイミングではなく、あらかじめ設定しておく必要があります。

|overridePendingTransition|overrideActivityTransition|
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/b999901a-da15-4bcb-b617-fe12cae42d85.gif)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/12a84571-3775-46a5-f36a-9cca6ce66d87.gif)|

# まとめ

予測型「戻る」ジェスチャーのアニメーションの指定もできるようになっているので、`overrideActivityTransition`への置き換えを進めていきたいですね。ただ、単純な置き換えはできないので、minSdkVersionを34にできるまでは、処理の分岐をあちこちで書かないといけないのが悩みどころですね。

----

# overrideActivityTransitionが使えない場合

これで万事解決かと思いきや、`overrideActivityTransition`が通用しない場合がありました。
Activityのテーマで、`android:windowShowWallpaper`や`android:windowIsTranslucent`を`true`にしている場合です。

```themes.xml
<style name="Theme.MyApplication2" parent="Base.Theme.MyApplication">
    <item name="android:windowShowWallpaper">true</item>
    <item name="android:windowBackground">@android:color/transparent</item>
</style>
```

この場合、以下のように、`overridePendingTransition`では指定できるのに、`overrideActivityTransition`では指定とは異なるアニメーションになってしまいます。（予測型「戻る」ジェスチャーには適用されるみたいですが……）

|overridePendingTransition|overrideActivityTransition|
|--|--|
|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/bc55d6d9-be98-d04c-7cb8-85fa10bdc71b.gif)|![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/895ffa8c-68f3-3b98-6396-f4a90ba52d2e.gif)|

## ActivityOptionを使う方法

開始についてはstartActivityのActivityOptionについてはまだDeprecatedになっていませんので、以下のようにするとこで開始アニメーションを変更できます。

```MainActivity.kt
val options = ActivityOptions.makeCustomAnimation(this, R.anim.slide_in_right, R.anim.slide_out_left).toBundle()
startActivity(Intent(this, MainActivity2::class.java), options)
```

しかし、閉じる方のアニメーションはこのような方法では指定できないようです。

## wallpaperXXXXAnimation を指定する方法

オープンクローズ両方を指定したい場合は、以下のようにstyleを作成し

```styles.xml
<style name="anim">
    <item name="android:wallpaperOpenEnterAnimation">@anim/slide_in_right</item>
    <item name="android:wallpaperOpenExitAnimation">@anim/slide_out_left</item>
    <item name="android:wallpaperCloseEnterAnimation">@anim/slide_in_left</item>
    <item name="android:wallpaperCloseExitAnimation">@anim/slide_out_right</item>
</style>
```

`window.setWindowAnimations`でActivityで指定します。

```MainActivity2.kt
window.setWindowAnimations(R.style.anim)
```

こうすることで、同様のアニメーションを実行することができるようになりました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155171/3bc3c6ee-adb4-3144-e6b2-2a91ac7a5dc5.gif)

以上です。