Activity/Fragment Transitionsのつかいかた

  • 211
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

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

収束・発散します
explode.gif

Fade

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

Slide

スライドイン・スライドアウトします。
setSlideEdge(int slideEdge)でスライドする方向が指定出来ます。

Slide
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);

slide.gif

ChangeBounds

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

ChangeImageTransform

ImageViewのmatrixが変化するようなTransitionです。
ChangeBoundsと一緒に使うことで位置・大きさ・スケールがスムーズに変化します。
image.gif

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を使うサンプルなのでお気をつけ下さい。

この投稿は Android Advent Calendar 201421日目の記事です。