fresh digitable

めんどくさかったなってことを振り返ったり振り返らなかったりするための記録

RecyclerViewの中の要素をshared elementsにしてアニメーションする

今作っているツイッタークライアントは、タイムラインのユーザアイコンがタップされたらユーザ情報Activityに遷移するのだが、そのときにタップされたユーザアイコンが移動してユーザ情報Activityのユーザアイコンに重なるアニメーションを実装した。現時点ではAndroid 5.0 (API level 21)以上で有効。

参考:developer.android.com

まずは遷移元(タイムライン)と先(ユーザ情報)のActivityにWindow.requestFeature()する。

// setContentView()の前に行う
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
}

遷移元と先のビューにユニークなtransitionNameをつける。RecyclerViewの中の要素にtransitionNameをつけるときはアニメーションさせようとする直前にセットしておかないとユニークにならないようで、カスタムビューの中でinflateした後にセットするとうまくいかなかった(タイムライン→ユーザ情報はうまくいくが、そこからbackボタンで戻るときにアニメーションの起点が右下とか左下になったりした)。クリックされたビューがアニメーションするので、クリックリスナの中でやってしまっている。

userIcon.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View view) {
    ViewCompat.setTransitionName(view, "user_icon");
  }
});

ActivityOptionを渡してstartActivity()する。同様に上のコードのクリックリスナの中でやっている。

Intent intent = new Intent(context, UserInfoActivity.class);
ActivityCompat.startActivity(this, intent,
    ActivityOptionsCompat.makeSceneTransitionAnimation(this, userIcon, "user_icon").toBundle());

UserInfoActivity.start(Activity, View)みたいなユーティリティメソッドの中でやってしまうのもありかもしれない。

public static Intent createIntent(Context context) {
  return new Intent(context, UserInfoActivity.class);
}

public static void start(Activity activity, View userIcon) {
  final Intent intent = createIntent(activity.getApplicationContext());
  ViewCompat.setTransitionName(userIcon, "user_icon");
  ActivityCompat.startActivity(activity, intent,
      ActivityOptionsCompat.makeSceneTransitionAnimation(activity, userIcon, "user_icon").toBundle());
}