CardViewのヘッダー画像をそのまま使い遷移する

#はじめに
お久しぶりです、[シオン](https://twitter.com/shion1118)です。
久しぶりにAndroidのアプリ開発をしていて詰まったので解決方法を共有したいと思います。
Google I/O 2018にてMaterial Designが刷新され、それに伴い[Material.io](https://material.io/)のガイドもかなり変わりました。

そんな中、[コンポーネントのCards](https://material.io/design/components/cards.html#behavior)に次のようなアニメーションがありました。

![](https://media.giphy.com/media/YVrR54dj2IOacVvKeV/giphy.gif)

かっこいい、、やりたい、、、！
と思いましたがこの画像を保ったまま遷移するのはどのように実装するのか、詰まっていました。
そもそもアニメーションの名前すらわからない:sob:
Google曰く、 

> A card expands to fill the full screen using a parent-child transition.
親子遷移を使い、カードをフルスクリーンに展開する。

いや、意味がわからない。
仕方がないので、自分で調べていくなかで、それっぽい名前を見つけました。

**Shared Element Transition** と言うらしい。
共通要素を使った遷移、まさにこれです:sunglasses:

ということで本記事では、この遷移を使い
さきほどの画像のようなアニメーションを実装していこうと思います。

:beginner:サンプルコードは[Github](https://github.com/shion1118/CardTransition)にあります

#実装

##準備
Material Componentsを使用したいため、最新のサポートライブラリを追加します。

```gradle:build.gradle
dependencies {
    ...
    implementation 'com.android.support:design:28.0.0-alpha3'
    implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
}
```

##遷移元のActivityを作る

遷移元となるActivityを作ります。
先ほどのアニメーションでいうところの、この画面です。

![animation1](https://i.imgur.com/h2yqGoN.jpg)

`MaterialCardView`を使いできるだけ寄せていきます。
僕は楽なのでLinerLayoutを使っています。
（もっといい書き方があると思うのでプルリクやコメントくれると嬉しいです:no_mouth:）

```xml
<android.support.design.card.MaterialCardView
        android:id="@+id/user_cardView"
        android:layout_width="match_parent"
        android:layout_height="266dp"
        app:cardCornerRadius="4dp">


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/header_imageView"
                android:layout_width="match_parent"
                android:layout_height="194dp"
                android:scaleType="centerCrop"
                app:srcCompat="@drawable/header" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="40dp"
                android:layout_margin="16dp"
                android:orientation="horizontal">

                <ImageView
                    android:id="@+id/icon_imageView"
                    android:layout_width="40dp"
                    android:layout_height="40dp"
                    android:layout_marginRight="16dp"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/icon" />

                <LinearLayout
                    android:layout_width="240dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:orientation="vertical">

                    <TextView
                        android:id="@+id/user_name_textView"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="@string/user_name"
                        android:textColor="@color/colorHeaderText"
                        android:textSize="20dp"
                        android:textStyle="bold" />

                    <TextView
                        android:id="@+id/user_profession_textView"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="@string/user_profession"
                        android:textColor="@color/colorSubheadText"
                        android:textSize="14dp" />
                </LinearLayout>

            </LinearLayout>
        </LinearLayout>
</android.support.design.card.MaterialCardView>
```
実際の画面はこんな感じです。

![MainActivity](https://i.imgur.com/cHaZnwm.jpg)

##遷移先のActivityを作る

次に遷移先となるActivityを作ります。
この画面です。

![animation2](https://i.imgur.com/xxYbCeG.png)

先ほどのレイアウトを元に作りました。
実装した画面はこんな感じです。

![UserActivity](https://i.imgur.com/VAL7KUM.jpg)

##共通要素を設定する

遷移元と遷移先でシェアをする共通の要素を設定します。
指定方法は、layoutファイルに`transitionName`で指定します。
`transitionName`は、遷移元、遷移先でそれぞれ共通となるようする必要があります。

今回は、MainActivityからUserActivityへの遷移です。
共通の要素は、以下の4つです。それぞれに同じ`transitionName`を指定します。

- **Header Image**   
  transionName : userHeader
- **User Icon**  
  transionName : userIcon
- **User Name**  
  transionName : userName
- **User Profession**  
  transionName : userProfession

このように指定することで、共通の要素として扱うことができます。


![](https://i.imgur.com/AaLxNEC.jpg)

例えば、ユーザーのヘッダー画像に対しては、次のように指定します。

```xml:activity_main.xml
...

<ImageView
    android:id="@+id/header_imageView"
    ...
    android:transitionName="userHeader"
    ...
 />

...
```

遷移先のヘッダー画像に対しても同様に指定します。

```xml:activity_user.xml
<ImageView
    android:id="@+id/user_header_imageView"
    ...
    android:transitionName="userHeader"
    ...
 />
```

これにより、遷移元と遷移先の各パーツを共通の要素として扱う準備ができました。
次にこの設定を元に遷移する処理を書いていきます。

##Activityを遷移する
遷移元のActivityにて、共通の要素を取得します。

```Java:MainActivity.java
userHeader = (ImageView) findViewById(R.id.header_imageView);
userIcon = (ImageView) findViewById(R.id.icon_imageView);
userName = (TextView) findViewById(R.id.name_textView);
userProfession = (TextView) findViewById(R.id.profession_textView);
```

`CardView`に`OnClickListener`を使いクリックされたときの処理を書いていきます。
まず、共通の要素と`transitionName`よりペアを作成します。
ヘッダー画像の場合は次のようにペアを作成します。

```Java
Pair.create((View) userHeader, "userHeader")
```

作成したペアを、遷移時のオプションとして設定します。

```Java
ActivityOptions options = ActivityOptions.
                              makeSceneTransitionAnimation(MainActivity.this,
                                  Pair.create((View) userHeader, "userHeader")
                              );
```

このオプションを`startActivity`に`Bundle`として与えることで
**Shared Element Transition** を行うことができます！

```Java
Intent intent = new Intent(MainActivity.this, UserActivity.class);
                startActivity(intent, options.toBundle());
```

カードをクリックしたときの最終的な処理は次のようになりました。

```Java:MainAcitivty.java
...

@Override
public void onClick(View view) {
    ActivityOptions options = ActivityOptions.
                                  makeSceneTransitionAnimation(MainActivity.this,
                                      Pair.create((View) userHeader, "userHeader"),
                                      Pair.create((View) userIcon, "userIcon"),
                                      Pair.create((View) userName, "userName"),
                                      Pair.create((View) userProfession, "userProfession")
                                  );

    Intent intent = new Intent(MainActivity.this, UserActivity.class);
    startActivity(intent, options.toBundle());
}
```

#まとめ
![](https://i.imgur.com/7oQniYg.gif)

最終的のこのようなアニメーションになりました！
色々、使い道が浮かんできます。
サンプルコードはGithubにあるので、指摘事項はコメントやIssueなどでください:bow_tone1:
[shion1118/CardTransition - Github](https://github.com/shion1118/CardTransition)

最後まで読んでいただき
ありがとうございました:wave_tone1: