Edited at
AndroidDay 21

Activity/Fragment Transitionsのつかいかた

More than 3 years have passed since last update.

Android Advent Calendar 2014 21日目の記事です。

LollipopでActivity Transitionsが追加されました。(https://developer.android.com/training/material/animations.html#Transitions)

Fragmentにも無いのかな?と探してみたところ普通にありましたので,あわせて新たに追加されたTransitionの使い方をまとめます。


できるようになったこと

今まではActivity/Fragmentの遷移時のアニメーションについては、それぞれの持つView 全体 に対して効果を指定していました。

LollipopからはTransitionを用いることにより、Activity/Fragmentの 各View についてアニメーション可能になりました。

例えばViewが画面外に発散したり、Viewを共有させたまま画面遷移するといったアニメーションです。


Transition

android.transitionはKitKatにて追加されたパッケージで、UIの異なる2つの状態について遷移アニメーションを自動的に行ってくれます。

以下では用意されているもののうち6つを紹介します。


Expode

収束・発散します


Fade

フェードイン・フェードアウトします。


Slide

スライドイン・スライドアウトします。

setSlideEdge(int slideEdge)でスライドする方向が指定出来ます。


Slide

Slide slide = new Slide();

slide.setSlideEdge(Gravity.RIGHT);


ChangeBounds

Viewの位置・大きさが変わるようなTransitionです。


ChangeImageTransform

ImageViewのmatrixが変化するようなTransitionです。

ChangeBoundsと一緒に使うことで位置・大きさ・スケールがスムーズに変化します。


TransitionSet

他のTransitionをまとめます。setOrdering(int ordering)にて順番に処理するか同時に処理するかを設定出来ます。


Transition

TransitionSet set = new TransitionSet();

set.addTransition(new ChangeBounds());
set.addTransition(new ChangeImageTransform());
//順番にTransitionを実行する
set.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);


Activity/Fragment Transitions実装の流れ

TransicionをActivityやFragmentの遷移に使うためには、以下のように記述します。


  1. window contentのTransitionの有効化

  2. Transitionの定義

  3. Activity/FragmentにTransitionの設定

  4. SharedElementの設定

  5. Activity/Fragmentの起動

ここからはFragment/Activityそれぞれについて使い方を説明していきます。


Fragment Trnasitions


1. window contentのTransitionの有効化

コードまたはxmlでwindow contentのTransitionを有効にします。

getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);


style.xml

<resources>

<style name="Theme.Base" parent="android:Theme.Material.Light">
<item name="android:windowContentTransitions">true</item>
</style>
</resources>


2. Transitionの定義

コードまたはxmlで遷移する際に実行されるTransitionを定義します。

TransitionSet ts = new TransitionSet();

ts.addTransition(new Fade());
ts.addTransition(new Slide());


res/transition/base_transition.xml

<transitionSet xmlns:android="http://schemas.android.com/apk/res/android"  >

<fade />
<slide />
</transitionSet>


3. FragmentにTransitionの設定

FragmentにTransitionを設定しますが、以下のパターンがあります。遷移元をFROM,遷移先をTOとすると


ExitTransition

FROMからTOを呼び出した時、FROMのViewが消えるアニメーション


EnterTransition

FROMからTOを呼び出した時、TOのViewが現れるアニメーション


ReturnTransition

TOからFROMに戻る時、TOのViewが消えるアニメーション

デフォルトだとEnterTransitionと同じものが使われます


ReenterTransition

TOからFROMに戻る時、FROMのViewが現れるアニメーション

デフォルトだとExitTransitionと同じものが使われます

以上のパターンについてFragmentにsetterが存在するので、2.で定義したTransitionを設定します。


Fragment

Fragment frag = new NextFragment();

frag.setEnterTransition(ts);

xmlで記述も出来ます。


style.xml

<resources>

<style name="Theme.Base" parent="android:Theme.Material.Light">
     <item name="android:fragmentEnterTransition">
@transition/base_transition
</item>
</style>
</resources>


4. SharedElementの設定

Viewを共有するような形でシームレスに遷移させる場合はSharedElementを設定します。

その際にはSharedElement用にTransitionを設定し、遷移前後で共有するViewに同じTransitionNameをつけなければなりません。

SharedElementを利用しなければ以下の設定は必要ありません。


遷移元Fragment

//Transitionを設定する

Frag frag = new TransitionFragment();
frag.setEnterTransition(set);
frag.setSharedElementEnterTransition(set);

//共有するViewにTransitionNameをつける
View target = findViewById(R.id.sharedElement);
target.setTransitionName("share");

//SharedElementとして設定する
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.container,frag).addSharedElement(target, transitionName);



遷移先Fragment

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_next, container, false);
View v = rootView.findViewById(R.id.sharedElement);
v.setTransitionName("share");

return rootView;
}



5. Fragmentの起動

あとは起動するだけです。まとめてみると


FragmentTransitionActivity.java

public class FragmentTransitionActivity extends Activity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new TransitionFragment())
.commit();
}
}

@Override
public void onClick(View v) {
TransitionSet set = new TransitionSet();
set.addTransition(new ChangeImageTransform());
set.addTransition(new ChangeBounds());

View target = v.findViewById(R.id.sharedElement);
Fragment frag = new NextActivity.PlaceholderFragment();
frag.setEnterTransition(set);
frag.setSharedElementEnterTransition(set);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.container,frag )
.addSharedElement(target, "share")
.addToBackStack(null)
.commit();
}

public static class TransitionFragment extends Fragment {
@Override
public View onCreateView (LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
View target = rootView.findViewById(R.id.sharedElement);
target.setTransitionName("share");
target.setOnClickListener((ForthActivity)getActivity());
return rootView;
}

}
}




Activity Transitions


1. window contentのTransitionの有効化

Fragmentと同様に設定します。


2. Transitionの定義

Fragmentと同様に定義します。


3. ActivityにTransitionの設定

Activityの場合はWindowに対してTransitionを設定します。

getWindow().setExitTransition(ts);  

getWindow().setEnterTransition(ts);


4. SharedElementの設定

Activityの場合は共有するViewに同じTransitionNameをつけ なくても いいみたいです。遷移先のViewにTransitionNameで設定しActivityOptions.makeSceneTransitionAnimation()で指定すればよさそうです。

SharedElementが1つの場合はそのままViewとTransitionNameを指定出来ますし、複数の場合はPairを使えます。

SharedElementを利用しない場合は、第2引数をnullにしてActivityOptionsを生成します。


遷移元Activity

//SharedElementを利用しない

ActivityOptions option0 = ActivityOptions.makeSceneTransitionAnimation(this, null);

//SharedElementが1つだけ
View target1 = findViewById(R.id.sharedElement1);
ActivityOptions options1 =
ActivityOptions.makeSceneTransitionAnimation(this, target1, "share");

//SharedElementが複数の場合
View target2 = findViewById(R.id.sharedElement2);
ActivityOptions options2 = ActivityOptions.makeSceneTransitionAnimation(this,
new Pair<View, String>(target1, "share1"),
new Pair<View, String>(target2, "share2"));



遷移先Activity

@Override

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View target1 = findViewById(R.id.sharedElement1);
target1.setTransitionName("share1");
View target2 = findViewById(R.id.sharedElement2);
target2.setTransitionName("share2");
}


5. Activityの起動

4.で生成したActivityOptionsを利用して起動します。

まとめるとこんな感じ。


ActivityTransitionActivity.java

public class ActivityTransitionActivity extends Activity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
setContentView(R.layout.fragment_main);
TransitionSet ts = new TransitionSet();
ts.addTransition(new Fade());
ts.addTransition(new Explode());
getWindow().setExitTransition(ts);
getWindow().setEnterTransition(ts);

View v = findViewById(R.id.image);
v.setOnClickListener(this);

}

@Override
public void onClick(View v) {
Intent intent = new Intent(this, NextActivity.class);
View target = v.findViewById(R.id.image);
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this, target, "share");
startActivity(intent, options.toBundle());
}
}



まとめ

Lollipopからは、Activity/Fragmentの遷移アニメーションについてより柔軟に設定が可能になりました。

特にSharedElementに関しては実装は簡単に行えるのにUI上の効果は大きいです。(Android Transisionsの通り従来の方法で実装しようとしましたが、断念。。)

実はAndroid studio 1.0のSampleに

ActivitySceneTransitionBasicがあるのでこれをImportして動かしてみるのが一番早いです。

よくあるMaster-Detail構成で、そのActivity遷移にTransitionを使っています。

SampleにFragment Transitionというのもありますがこれは本記事の内容とは違いTransitionでなくAnimationを使うサンプルなのでお気をつけ下さい。