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
収束・発散します
##Slide
スライドイン・スライドアウトします。
setSlideEdge(int slideEdge)
でスライドする方向が指定出来ます。
Slide slide = new Slide();
slide.setSlideEdge(Gravity.RIGHT);
##ChangeBounds
Viewの位置・大きさが変わるようなTransitionです。
##ChangeImageTransform
ImageViewのmatrixが変化するようなTransitionです。
ChangeBounds
と一緒に使うことで位置・大きさ・スケールがスムーズに変化します。
##TransitionSet
他のTransitionをまとめます。setOrdering(int ordering)
にて順番に処理するか同時に処理するかを設定出来ます。
TransitionSet set = new TransitionSet();
set.addTransition(new ChangeBounds());
set.addTransition(new ChangeImageTransform());
//順番にTransitionを実行する
set.setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
Activity/Fragment Transitions実装の流れ
TransicionをActivityやFragmentの遷移に使うためには、以下のように記述します。
- window contentのTransitionの有効化
- Transitionの定義
- Activity/FragmentにTransitionの設定
- SharedElementの設定
- Activity/Fragmentの起動
ここからはFragment/Activityそれぞれについて使い方を説明していきます。
Fragment Trnasitions
###1. window contentのTransitionの有効化
コードまたはxmlでwindow contentのTransitionを有効にします。
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
<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());
<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 frag = new NextFragment();
frag.setEnterTransition(ts);
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を利用しなければ以下の設定は必要ありません。
//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);
@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の起動
あとは起動するだけです。まとめてみると
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を生成します。
//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"));
@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を利用して起動します。
まとめるとこんな感じ。
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を使うサンプルなのでお気をつけ下さい。