zukkeyの技術奮闘記

”当たり前”が誰かのためになる、はず

SharedElementTransition: 基礎編

はじめに

今回はSharedElementTransitionの基礎編としてSharedElementを用いてマテリアルデザインに沿った遷移の基本をサンプルを用いて書き残しておこうと思います。

 

今回やっていることで参考にしている資料は、DroidKaigiで実例で理解するマテリアルデザインアニメーションというスライドです。

 

 

まず最初に実際にサンプルの動きを確認する

f:id:rozkey59:20180218132655g:plain

 

FABをボタンにして今回はやっています。

Activityから次のActivityに遷移する際に拡大しながらアニメーションするものです。

どのように実現するのか実際のコードを見ていきましょう。

 

遷移を実現するためのポイント

この遷移を実現するのに必要なポイントは以下の2点です。

  1. レイアウトにtransitionNameをつける
  2. 必要な情報をBundleに詰めて渡す

実際のコードを見ていきましょう。

 

レイアウトにtransitionNameをつける

今回は画像のViewを共有したいのでレイアウトにユニークな名前を設定してあげる必要があります。遷移前と遷移後のレイアウト両方で設定する必要があります。

 

content_main.xml

ちょっとややこしいですが遷移前の小さい画像のレイアウトの部分になります。

<layout xmlns:tools="http://schemas.android.com/tools">

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionName="@string/transition"
android:src="@drawable/android"
tools:ignore="ContentDescription"/>

</android.support.constraint.ConstraintLayout>
</layout>

ImageViewの中でtransitioNameをつけています。名前はどんなものでもOKです。

xmlの中で設定しても良いですが、コードで指定することもできます。

自分の場合は分かりやすいのでxmlで完結しています。

 

activity_next.xml

遷移後の画面になります。

<layout xmlns:tools="http://schemas.android.com/tools">

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionName="@string/transition"
android:src="@drawable/android"
tools:ignore="ContentDescription"/>

</android.support.constraint.ConstraintLayout>
</layout>

こちらは画像が大きくなった時のImageViewに遷移前と同じtransitionNameを設定してあげています。

 

strings.xml

transitionNameに設定している名前です。

直に設定するよりここで設定してここからxmlで定義するのが良いと思います。

<resources>
<string name="app_name">SharedElementTransition</string>
<string name="action_settings">Settings</string>
<string name="transition">transition:android</string>
</resources>

 

必要な情報をBundleに詰めて渡す

遷移前で以下のように必要な情報をBundleに詰めてあげて渡してください。

binding.fab.setOnClickListener { view ->
val bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
binding.contentMain?.image,
getString(R.string.transition)
).toBundle()
ActivityCompat.startActivity(
this,
Intent(this, NextActivity::class.java),
bundle)
}

fab(フローティングアクションボタン)をタップしたときにbundleを作ってここで必要な情報を詰めます。一つの場合はこれだけで良いです。

 

defaultで設定されているので広がるアニメーションをつける場合はたったこれだけで上記のような遷移が実現できます。

 

用意するActivityやレイアウトについて

一応サンプルのコードを載せておきます。

以下リンクからクローンしてビルドも出来ますのでやってみてください。

GitHub - yutaro6547/SharedElementTransition

Activity

遷移前のActivityと遷移後のActivityを用意してください。

僕の場合は、MainActivity(遷移前)とNextActivity(遷移後)のように用意しています。

MainActivity

今回はDataBindingを使用しています。

実際のコードは以下になります。

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
setSupportActionBar(binding.toolbar)
binding.fab.setOnClickListener { view ->
val bundle = ActivityOptionsCompat.makeSceneTransitionAnimation(
this,
binding.contentMain?.image,
getString(R.string.transition)
).toBundle()
ActivityCompat.startActivity(
this,
Intent(this, NextActivity::class.java),
bundle)
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
}

 

NextActivity

遷移後のActivityは表示するだけなのでこのぐらいです。

class NextActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_next)
DataBindingUtil.setContentView<ActivityNextBinding>(this, R.layout.activity_next)
}
}

 

レイアウト

activity_main

遷移前のレイアウトになります。

DataBindingを利用しているので<layout>でくくっています。

<?xml version="1.0" encoding="utf-8"?>
<layout>

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="zukkey.sharedelementtransition.MainActivity">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay"/>

</android.support.design.widget.AppBarLayout>

<include
android:id="@+id/contentMain"
layout="@layout/content_main"/>

<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
tools:ignore="ContentDescription"
app:srcCompat="@android:drawable/ic_dialog_email"/>

</android.support.design.widget.CoordinatorLayout>
</layout>

 

content_main

メイン画面の中身のレイアウトになります。

<layout>

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="zukkey.sharedelementtransition.MainActivity"
tools:showIn="@layout/activity_main">

<ImageView
android:id="@+id/image"
android:layout_width="100dp"
android:layout_height="100dp"
android:transitionName="@string/transition"
android:src="@drawable/android"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

</android.support.constraint.ConstraintLayout>
</layout>

 

activity_next

遷移後のレイアウトになります。

<layout xmlns:tools="http://schemas.android.com/tools">

<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:transitionName="@string/transition"
android:src="@drawable/android"
tools:ignore="ContentDescription"/>

</android.support.constraint.ConstraintLayout>
</layout>

 

さいごに

今回はSharedElementTransitionの基本についての解説と備忘録でした。

スライドを基にやってみたという話ですが、実際に手を動かして書いてみると理解が深まるのでやってみてよかったです。

複雑なレイアウトになったり、複数共有する場合はもうひと手間必要ですが自分も試行錯誤中なので分かり次第またサンプルを作って投稿していきたいと思います。